@@ -414,6 +414,65 @@ def test_authed_media_redirect_response(self) -> None:
414414 self .assertEqual (length , len (data ))
415415 self .assertEqual (output_stream .getvalue (), data )
416416
417+ @override_config (
418+ {
419+ "federation" : {
420+ # Set the timeout to a deterministic value, in case the defaults
421+ # change.
422+ "client_timeout" : "10s" ,
423+ }
424+ }
425+ )
426+ def test_authed_media_timeout_reading_body (self ) -> None :
427+ """
428+ If the HTTP request is connected, but gets no response before being
429+ timed out, it'll give a RequestSendFailed with can_retry.
430+
431+ Regression test for https://github.com/element-hq/synapse/issues/19061
432+ """
433+ limiter = Ratelimiter (
434+ store = self .hs .get_datastores ().main ,
435+ clock = self .clock ,
436+ cfg = RatelimitSettings (key = "" , per_second = 0.17 , burst_count = 1048576 ),
437+ )
438+
439+ output_stream = io .BytesIO ()
440+
441+ d = defer .ensureDeferred (
442+ # timeout is set by `client_timeout`, which we override above.
443+ self .cl .federation_get_file (
444+ "testserv:8008" , "path" , output_stream , limiter , "127.0.0.1" , 10000
445+ )
446+ )
447+
448+ self .pump ()
449+
450+ conn = Mock ()
451+ clients = self .reactor .tcpClients
452+ client = clients [0 ][2 ].buildProtocol (None )
453+ client .makeConnection (conn )
454+
455+ # Deferred does not have a result
456+ self .assertNoResult (d )
457+
458+ # Send it the HTTP response
459+ client .dataReceived (
460+ b"HTTP/1.1 200 OK\r \n "
461+ b"Server: Fake\r \n "
462+ # Set a large content length, prompting the federation client to
463+ # wait to receive the rest of the body.
464+ b"Content-Length: 1000\r \n "
465+ b"Content-Type: multipart/mixed; boundary=6067d4698f8d40a0a794ea7d7379d53a\r \n \r \n "
466+ )
467+
468+ # Push by enough to time it out
469+ self .reactor .advance (10.5 )
470+ f = self .failureResultOf (d )
471+
472+ self .assertIsInstance (f .value , RequestSendFailed )
473+ self .assertTrue (f .value .can_retry )
474+ self .assertIsInstance (f .value .inner_exception , defer .TimeoutError )
475+
417476 @parameterized .expand (["get_json" , "post_json" , "delete_json" , "put_json" ])
418477 def test_timeout_reading_body (self , method_name : str ) -> None :
419478 """
0 commit comments