-
Notifications
You must be signed in to change notification settings - Fork 349
Re-introduce federation /download endpoint #17350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
33c2e91
aeeeb95
c2663f5
89c866f
6dac5fb
c48f39d
3851206
86208eb
9e85943
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Support [MSC3916](https://github.com/matrix-org/matrix-spec-proposals/blob/rav/authentication-for-media/proposals/3916-authentication-for-media.md) | ||
by adding a federation /download endpoint (#17350). | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,7 +25,16 @@ | |
import urllib | ||
from abc import ABC, abstractmethod | ||
from types import TracebackType | ||
from typing import Awaitable, Dict, Generator, List, Optional, Tuple, Type | ||
from typing import ( | ||
TYPE_CHECKING, | ||
Awaitable, | ||
Dict, | ||
Generator, | ||
List, | ||
Optional, | ||
Tuple, | ||
Type, | ||
) | ||
|
||
import attr | ||
|
||
|
@@ -37,8 +46,13 @@ | |
from synapse.http.server import finish_request, respond_with_json | ||
from synapse.http.site import SynapseRequest | ||
from synapse.logging.context import make_deferred_yieldable | ||
from synapse.util import Clock | ||
from synapse.util.stringutils import is_ascii | ||
|
||
if TYPE_CHECKING: | ||
from synapse.storage.databases.main.media_repository import LocalMedia | ||
|
||
|
||
logger = logging.getLogger(__name__) | ||
|
||
# list all text content types that will have the charset default to UTF-8 when | ||
|
@@ -260,6 +274,70 @@ def _can_encode_filename_as_token(x: str) -> bool: | |
return True | ||
|
||
|
||
async def respond_with_multipart_responder( | ||
clock: Clock, | ||
request: SynapseRequest, | ||
responder: "Optional[Responder]", | ||
media_info: "LocalMedia", | ||
) -> None: | ||
""" | ||
Responds to requests originating from the federation media `/download` endpoint by | ||
streaming a multipart/mixed response | ||
|
||
Args: | ||
request: the federation request to respond to | ||
H-Shay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
responder: the responder which will send the response | ||
media_info: metadata about the media item | ||
""" | ||
if not responder: | ||
respond_404(request) | ||
return | ||
|
||
# If we have a responder we *must* use it as a context manager. | ||
with responder: | ||
if request._disconnected: | ||
logger.warning( | ||
"Not sending response to request %s, already disconnected.", request | ||
) | ||
return | ||
|
||
from synapse.media.media_storage import MultipartFileConsumer | ||
|
||
# note that currently the json_object is just {}, this will change when linked media | ||
# is implemented | ||
multipart_consumer = MultipartFileConsumer( | ||
clock, request, media_info.media_type, {} | ||
) | ||
|
||
logger.debug("Responding to media request with responder %s", responder) | ||
# ensure that the response length takes into account the multipart boundary and headers, | ||
# which is currently 177 bytes (note that this will need to be determined dynamically when | ||
# the json object passed into the multipart file consumer isn't just {}) | ||
if media_info.media_length is not None: | ||
request.setHeader( | ||
b"Content-Length", b"%d" % (media_info.media_length + 177,) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if we should make this calculation in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that's a great idea - I have done so |
||
) | ||
|
||
request.setHeader( | ||
b"Content-Type", | ||
b"multipart/mixed; boundary=%s" % multipart_consumer.boundary, | ||
) | ||
|
||
try: | ||
await responder.write_to_consumer(multipart_consumer) | ||
except Exception as e: | ||
# The majority of the time this will be due to the client having gone | ||
# away. Unfortunately, Twisted simply throws a generic exception at us | ||
# in that case. | ||
logger.warning("Failed to write to consumer: %s %s", type(e), e) | ||
|
||
# Unregister the producer, if it has one, so Twisted doesn't complain | ||
if request.producer: | ||
request.unregisterProducer() | ||
|
||
finish_request(request) | ||
|
||
|
||
async def respond_with_responder( | ||
request: SynapseRequest, | ||
responder: "Optional[Responder]", | ||
|
Uh oh!
There was an error while loading. Please reload this page.