mirror of
https://github.com/element-hq/synapse
synced 2024-07-06 23:43:34 +00:00
make sure we are adding content-disposition headers to multipart response
This commit is contained in:
parent
e9838e40f3
commit
ae65155d9f
|
@ -221,6 +221,7 @@ def add_file_headers(
|
||||||
# select private. don't bother setting Expires as all our
|
# select private. don't bother setting Expires as all our
|
||||||
# clients are smart enough to be happy with Cache-Control
|
# clients are smart enough to be happy with Cache-Control
|
||||||
request.setHeader(b"Cache-Control", b"public,max-age=86400,s-maxage=86400")
|
request.setHeader(b"Cache-Control", b"public,max-age=86400,s-maxage=86400")
|
||||||
|
|
||||||
if file_size is not None:
|
if file_size is not None:
|
||||||
request.setHeader(b"Content-Length", b"%d" % (file_size,))
|
request.setHeader(b"Content-Length", b"%d" % (file_size,))
|
||||||
|
|
||||||
|
@ -302,12 +303,37 @@ async def respond_with_multipart_responder(
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if media_info.media_type.lower().split(";", 1)[0] in INLINE_CONTENT_TYPES:
|
||||||
|
disposition = "inline"
|
||||||
|
else:
|
||||||
|
disposition = "attachment"
|
||||||
|
|
||||||
|
def _quote(x: str) -> str:
|
||||||
|
return urllib.parse.quote(x.encode("utf-8"))
|
||||||
|
|
||||||
|
if media_info.upload_name:
|
||||||
|
if _can_encode_filename_as_token(media_info.upload_name):
|
||||||
|
disposition = "%s; filename=%s" % (
|
||||||
|
disposition,
|
||||||
|
media_info.upload_name,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
disposition = "%s; filename*=utf-8''%s" % (
|
||||||
|
disposition,
|
||||||
|
_quote(media_info.upload_name),
|
||||||
|
)
|
||||||
|
|
||||||
from synapse.media.media_storage import MultipartFileConsumer
|
from synapse.media.media_storage import MultipartFileConsumer
|
||||||
|
|
||||||
# note that currently the json_object is just {}, this will change when linked media
|
# note that currently the json_object is just {}, this will change when linked media
|
||||||
# is implemented
|
# is implemented
|
||||||
multipart_consumer = MultipartFileConsumer(
|
multipart_consumer = MultipartFileConsumer(
|
||||||
clock, request, media_info.media_type, {}, media_info.media_length
|
clock,
|
||||||
|
request,
|
||||||
|
media_info.media_type,
|
||||||
|
{},
|
||||||
|
disposition,
|
||||||
|
media_info.media_length,
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug("Responding to media request with responder %s", responder)
|
logger.debug("Responding to media request with responder %s", responder)
|
||||||
|
|
|
@ -401,13 +401,14 @@ class MultipartFileConsumer:
|
||||||
wrapped_consumer: interfaces.IConsumer,
|
wrapped_consumer: interfaces.IConsumer,
|
||||||
file_content_type: str,
|
file_content_type: str,
|
||||||
json_object: JsonDict,
|
json_object: JsonDict,
|
||||||
content_length: Optional[int] = None,
|
disposition: str,
|
||||||
|
content_length: Optional[int],
|
||||||
) -> None:
|
) -> None:
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
self.wrapped_consumer = wrapped_consumer
|
self.wrapped_consumer = wrapped_consumer
|
||||||
self.json_field = json_object
|
self.json_field = json_object
|
||||||
self.json_field_written = False
|
self.json_field_written = False
|
||||||
self.content_type_written = False
|
self.file_headers_written = False
|
||||||
self.file_content_type = file_content_type
|
self.file_content_type = file_content_type
|
||||||
self.boundary = uuid4().hex.encode("ascii")
|
self.boundary = uuid4().hex.encode("ascii")
|
||||||
|
|
||||||
|
@ -420,6 +421,7 @@ class MultipartFileConsumer:
|
||||||
self.paused = False
|
self.paused = False
|
||||||
|
|
||||||
self.length = content_length
|
self.length = content_length
|
||||||
|
self.disposition = disposition
|
||||||
|
|
||||||
### IConsumer APIs ###
|
### IConsumer APIs ###
|
||||||
|
|
||||||
|
@ -488,11 +490,13 @@ class MultipartFileConsumer:
|
||||||
self.json_field_written = True
|
self.json_field_written = True
|
||||||
|
|
||||||
# if we haven't written the content type yet, do so
|
# if we haven't written the content type yet, do so
|
||||||
if not self.content_type_written:
|
if not self.file_headers_written:
|
||||||
type = self.file_content_type.encode("utf-8")
|
type = self.file_content_type.encode("utf-8")
|
||||||
content_type = Header(b"Content-Type", type)
|
content_type = Header(b"Content-Type", type)
|
||||||
self.wrapped_consumer.write(bytes(content_type) + CRLF + CRLF)
|
self.wrapped_consumer.write(bytes(content_type) + CRLF)
|
||||||
self.content_type_written = True
|
disp_header = Header(b"Content-Disposition", self.disposition)
|
||||||
|
self.wrapped_consumer.write(bytes(disp_header) + CRLF + CRLF)
|
||||||
|
self.file_headers_written = True
|
||||||
|
|
||||||
self.wrapped_consumer.write(data)
|
self.wrapped_consumer.write(data)
|
||||||
|
|
||||||
|
@ -506,7 +510,6 @@ class MultipartFileConsumer:
|
||||||
producing data for good.
|
producing data for good.
|
||||||
"""
|
"""
|
||||||
assert self.producer is not None
|
assert self.producer is not None
|
||||||
|
|
||||||
self.paused = True
|
self.paused = True
|
||||||
self.producer.stopProducing()
|
self.producer.stopProducing()
|
||||||
|
|
||||||
|
@ -518,7 +521,6 @@ class MultipartFileConsumer:
|
||||||
the time being, and to stop until C{resumeProducing()} is called.
|
the time being, and to stop until C{resumeProducing()} is called.
|
||||||
"""
|
"""
|
||||||
assert self.producer is not None
|
assert self.producer is not None
|
||||||
|
|
||||||
self.paused = True
|
self.paused = True
|
||||||
|
|
||||||
if self.streaming:
|
if self.streaming:
|
||||||
|
@ -549,7 +551,7 @@ class MultipartFileConsumer:
|
||||||
"""
|
"""
|
||||||
if not self.length:
|
if not self.length:
|
||||||
return None
|
return None
|
||||||
# calculate length of json field and content-type header
|
# calculate length of json field and content-type, disposition headers
|
||||||
json_field = json.dumps(self.json_field)
|
json_field = json.dumps(self.json_field)
|
||||||
json_bytes = json_field.encode("utf-8")
|
json_bytes = json_field.encode("utf-8")
|
||||||
json_length = len(json_bytes)
|
json_length = len(json_bytes)
|
||||||
|
@ -558,9 +560,13 @@ class MultipartFileConsumer:
|
||||||
content_type = Header(b"Content-Type", type)
|
content_type = Header(b"Content-Type", type)
|
||||||
type_length = len(bytes(content_type))
|
type_length = len(bytes(content_type))
|
||||||
|
|
||||||
# 154 is the length of the elements that aren't variable, ie
|
disp = self.disposition.encode("utf-8")
|
||||||
|
disp_header = Header(b"Content-Disposition", disp)
|
||||||
|
disp_length = len(bytes(disp_header))
|
||||||
|
|
||||||
|
# 156 is the length of the elements that aren't variable, ie
|
||||||
# CRLFs and boundary strings, etc
|
# CRLFs and boundary strings, etc
|
||||||
self.length += json_length + type_length + 154
|
self.length += json_length + type_length + disp_length + 156
|
||||||
|
|
||||||
return self.length
|
return self.length
|
||||||
|
|
||||||
|
@ -569,7 +575,6 @@ class MultipartFileConsumer:
|
||||||
async def _resumeProducingRepeatedly(self) -> None:
|
async def _resumeProducingRepeatedly(self) -> None:
|
||||||
assert self.producer is not None
|
assert self.producer is not None
|
||||||
assert not self.streaming
|
assert not self.streaming
|
||||||
|
|
||||||
producer = cast("interfaces.IPullProducer", self.producer)
|
producer = cast("interfaces.IPullProducer", self.producer)
|
||||||
|
|
||||||
self.paused = False
|
self.paused = False
|
||||||
|
|
Loading…
Reference in a new issue