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

Support rendering some media downloads as inline #15988

Merged
merged 18 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/15988.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Render plain, CSS, CSV, JSON and XML media content in the browser (inline) when requested through the /download endpoint.
17 changes: 16 additions & 1 deletion synapse/media/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@
"text/xml",
]

# A list of all content types that are "safe" to be rendered inline in a browser.
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved
INLINE_CONTENT_TYPES = [
"text/css",
"text/plain",
"text/csv",
"application/json",
"application/ld+json",
"text/xml",
]
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved


def parse_media_id(request: Request) -> Tuple[str, str, Optional[str]]:
"""Parses the server name, media ID and optional file name from the request URI
Expand Down Expand Up @@ -155,7 +165,12 @@ def _quote(x: str) -> str:

# Use a Content-Disposition of attachment to force download of media.
disposition = "attachment"
if upload_name:

# We allow a strict subset of content types to be inlined
# so that they may be viewed directly in a browser.
if media_type.lower() in INLINE_CONTENT_TYPES:
disposition = "inline"
elif upload_name:
# RFC6266 section 4.1 [1] defines both `filename` and `filename*`.
#
# `filename` is defined to be a `value`, which is defined by RFC2616
Expand Down
19 changes: 18 additions & 1 deletion tests/media/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from synapse.media._base import get_filename_from_headers
from unittest.mock import Mock

from synapse.media._base import add_file_headers, get_filename_from_headers

from tests import unittest

Expand All @@ -36,3 +38,18 @@ def tests(self) -> None:
expected,
f"expected output for {hdr!r} to be {expected} but was {res}",
)


class AddFileHeadersTests(unittest.TestCase):
TEST_CASES = {
"text/plain": b"inline",
"text/xml": b"inline",
"text/html": b"attachment; filename=file.name",
"any/thing": b"attachment; filename=file.name",
}

def tests(self) -> None:
for media_type, expected in self.TEST_CASES.items():
request = Mock()
add_file_headers(request, media_type, 0, "file.name")
request.setHeader.assert_any_call(b"Content-Disposition", expected)
Loading