Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit f5c6a80

Browse files
H-Shaysquahtx
andauthored
Handle missing Content-Type header when accessing remote media (#11200)
* add code to handle missing content-type header and a test to verify that it works * add handling for missing content-type in the /upload endpoint as well * slightly refactor test code to put private method in approriate place * handle possible null value for content-type when pulling from the local db * add changelog * refactor test and add code to handle missing content-type in cached remote media * requested changes * Update changelog.d/11200.bugfix Co-authored-by: Sean Quah <[email protected]> Co-authored-by: Sean Quah <[email protected]>
1 parent e81fa92 commit f5c6a80

File tree

4 files changed

+29
-4
lines changed

4 files changed

+29
-4
lines changed

changelog.d/11200.bugfix

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a long-standing bug wherein a missing `Content-Type` header when downloading remote media would cause Synapse to throw an error.

synapse/rest/media/v1/media_repository.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ async def get_local_media(
215215
self.mark_recently_accessed(None, media_id)
216216

217217
media_type = media_info["media_type"]
218+
if not media_type:
219+
media_type = "application/octet-stream"
218220
media_length = media_info["media_length"]
219221
upload_name = name if name else media_info["upload_name"]
220222
url_cache = media_info["url_cache"]
@@ -333,6 +335,9 @@ async def _get_remote_media_impl(
333335
logger.info("Media is quarantined")
334336
raise NotFoundError()
335337

338+
if not media_info["media_type"]:
339+
media_info["media_type"] = "application/octet-stream"
340+
336341
responder = await self.media_storage.fetch_media(file_info)
337342
if responder:
338343
return responder, media_info
@@ -354,6 +359,8 @@ async def _get_remote_media_impl(
354359
raise e
355360

356361
file_id = media_info["filesystem_id"]
362+
if not media_info["media_type"]:
363+
media_info["media_type"] = "application/octet-stream"
357364
file_info = FileInfo(server_name, file_id)
358365

359366
# We generate thumbnails even if another process downloaded the media
@@ -445,7 +452,10 @@ async def _download_remote_file(
445452

446453
await finish()
447454

448-
media_type = headers[b"Content-Type"][0].decode("ascii")
455+
if b"Content-Type" in headers:
456+
media_type = headers[b"Content-Type"][0].decode("ascii")
457+
else:
458+
media_type = "application/octet-stream"
449459
upload_name = get_filename_from_headers(headers)
450460
time_now_ms = self.clock.time_msec()
451461

synapse/rest/media/v1/upload_resource.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ async def _async_render_POST(self, request: SynapseRequest) -> None:
8080
assert content_type_headers # for mypy
8181
media_type = content_type_headers[0].decode("ascii")
8282
else:
83-
raise SynapseError(msg="Upload request missing 'Content-Type'", code=400)
83+
media_type = "application/octet-stream"
8484

8585
# if headers.hasHeader(b"Content-Disposition"):
8686
# disposition = headers.getRawHeaders(b"Content-Disposition")[0]

tests/rest/media/v1/test_media_storage.py

+16-2
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def prepare(self, reactor, clock, hs):
248248

249249
self.media_id = "example.com/12345"
250250

251-
def _req(self, content_disposition):
251+
def _req(self, content_disposition, include_content_type=True):
252252

253253
channel = make_request(
254254
self.reactor,
@@ -271,8 +271,11 @@ def _req(self, content_disposition):
271271

272272
headers = {
273273
b"Content-Length": [b"%d" % (len(self.test_image.data))],
274-
b"Content-Type": [self.test_image.content_type],
275274
}
275+
276+
if include_content_type:
277+
headers[b"Content-Type"] = [self.test_image.content_type]
278+
276279
if content_disposition:
277280
headers[b"Content-Disposition"] = [content_disposition]
278281

@@ -285,6 +288,17 @@ def _req(self, content_disposition):
285288

286289
return channel
287290

291+
def test_handle_missing_content_type(self):
292+
channel = self._req(
293+
b"inline; filename=out" + self.test_image.extension,
294+
include_content_type=False,
295+
)
296+
headers = channel.headers
297+
self.assertEqual(channel.code, 200)
298+
self.assertEqual(
299+
headers.getRawHeaders(b"Content-Type"), [b"application/octet-stream"]
300+
)
301+
288302
def test_disposition_filename_ascii(self):
289303
"""
290304
If the filename is filename=<ascii> then Synapse will decode it as an

0 commit comments

Comments
 (0)