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

Commit 1b33847

Browse files
authored
Allow bigger responses to /federation/v1/state (#12877)
* Refactor HTTP response size limits Rather than passing a separate `max_response_size` down the stack, make it an attribute of the `parser`. * Allow bigger responses on `federation/v1/state` `/state` can return huge responses, so we need to handle that.
1 parent 4660d9f commit 1b33847

File tree

4 files changed

+19
-32
lines changed

4 files changed

+19
-32
lines changed

changelog.d/12877.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a bug introduced in Synapse 1.54 which could sometimes cause exceptions when handling federated traffic.

synapse/federation/transport/client.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@
4949

5050
logger = logging.getLogger(__name__)
5151

52-
# Send join responses can be huge, so we set a separate limit here. The response
53-
# is parsed in a streaming manner, which helps alleviate the issue of memory
54-
# usage a bit.
55-
MAX_RESPONSE_SIZE_SEND_JOIN = 500 * 1024 * 1024
56-
5752

5853
class TransportLayerClient:
5954
"""Sends federation HTTP requests to other servers"""
@@ -349,7 +344,6 @@ async def send_join_v1(
349344
path=path,
350345
data=content,
351346
parser=SendJoinParser(room_version, v1_api=True),
352-
max_response_size=MAX_RESPONSE_SIZE_SEND_JOIN,
353347
)
354348

355349
async def send_join_v2(
@@ -372,7 +366,6 @@ async def send_join_v2(
372366
args=query_params,
373367
data=content,
374368
parser=SendJoinParser(room_version, v1_api=False),
375-
max_response_size=MAX_RESPONSE_SIZE_SEND_JOIN,
376369
)
377370

378371
async def send_leave_v1(
@@ -1360,6 +1353,11 @@ class SendJoinParser(ByteParser[SendJoinResponse]):
13601353

13611354
CONTENT_TYPE = "application/json"
13621355

1356+
# /send_join responses can be huge, so we override the size limit here. The response
1357+
# is parsed in a streaming manner, which helps alleviate the issue of memory
1358+
# usage a bit.
1359+
MAX_RESPONSE_SIZE = 500 * 1024 * 1024
1360+
13631361
def __init__(self, room_version: RoomVersion, v1_api: bool):
13641362
self._response = SendJoinResponse([], [], event_dict={})
13651363
self._room_version = room_version
@@ -1427,6 +1425,9 @@ class _StateParser(ByteParser[StateRequestResponse]):
14271425

14281426
CONTENT_TYPE = "application/json"
14291427

1428+
# As with /send_join, /state responses can be huge.
1429+
MAX_RESPONSE_SIZE = 500 * 1024 * 1024
1430+
14301431
def __init__(self, room_version: RoomVersion):
14311432
self._response = StateRequestResponse([], [])
14321433
self._room_version = room_version

synapse/http/matrixfederationclient.py

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,6 @@
9292
"synapse_http_matrixfederationclient_responses", "", ["method", "code"]
9393
)
9494

95-
# a federation response can be rather large (eg a big state_ids is 50M or so), so we
96-
# need a generous limit here.
97-
MAX_RESPONSE_SIZE = 100 * 1024 * 1024
9895

9996
MAX_LONG_RETRIES = 10
10097
MAX_SHORT_RETRIES = 3
@@ -116,6 +113,11 @@ class ByteParser(ByteWriteable, Generic[T], abc.ABC):
116113
the content type doesn't match we fail the request.
117114
"""
118115

116+
# a federation response can be rather large (eg a big state_ids is 50M or so), so we
117+
# need a generous limit here.
118+
MAX_RESPONSE_SIZE: int = 100 * 1024 * 1024
119+
"""The largest response this parser will accept."""
120+
119121
@abc.abstractmethod
120122
def finish(self) -> T:
121123
"""Called when response has finished streaming and the parser should
@@ -203,7 +205,6 @@ async def _handle_response(
203205
response: IResponse,
204206
start_ms: int,
205207
parser: ByteParser[T],
206-
max_response_size: Optional[int] = None,
207208
) -> T:
208209
"""
209210
Reads the body of a response with a timeout and sends it to a parser
@@ -215,15 +216,12 @@ async def _handle_response(
215216
response: response to the request
216217
start_ms: Timestamp when request was made
217218
parser: The parser for the response
218-
max_response_size: The maximum size to read from the response, if None
219-
uses the default.
220219
221220
Returns:
222221
The parsed response
223222
"""
224223

225-
if max_response_size is None:
226-
max_response_size = MAX_RESPONSE_SIZE
224+
max_response_size = parser.MAX_RESPONSE_SIZE
227225

228226
try:
229227
check_content_type_is(response.headers, parser.CONTENT_TYPE)
@@ -240,7 +238,7 @@ async def _handle_response(
240238
"{%s} [%s] JSON response exceeded max size %i - %s %s",
241239
request.txn_id,
242240
request.destination,
243-
MAX_RESPONSE_SIZE,
241+
max_response_size,
244242
request.method,
245243
request.uri.decode("ascii"),
246244
)
@@ -772,7 +770,6 @@ async def put_json(
772770
backoff_on_404: bool = False,
773771
try_trailing_slash_on_400: bool = False,
774772
parser: Literal[None] = None,
775-
max_response_size: Optional[int] = None,
776773
) -> Union[JsonDict, list]:
777774
...
778775

@@ -790,7 +787,6 @@ async def put_json(
790787
backoff_on_404: bool = False,
791788
try_trailing_slash_on_400: bool = False,
792789
parser: Optional[ByteParser[T]] = None,
793-
max_response_size: Optional[int] = None,
794790
) -> T:
795791
...
796792

@@ -807,7 +803,6 @@ async def put_json(
807803
backoff_on_404: bool = False,
808804
try_trailing_slash_on_400: bool = False,
809805
parser: Optional[ByteParser] = None,
810-
max_response_size: Optional[int] = None,
811806
):
812807
"""Sends the specified json data using PUT
813808
@@ -843,8 +838,6 @@ async def put_json(
843838
enabled.
844839
parser: The parser to use to decode the response. Defaults to
845840
parsing as JSON.
846-
max_response_size: The maximum size to read from the response, if None
847-
uses the default.
848841
849842
Returns:
850843
Succeeds when we get a 2xx HTTP response. The
@@ -895,7 +888,6 @@ async def put_json(
895888
response,
896889
start_ms,
897890
parser=parser,
898-
max_response_size=max_response_size,
899891
)
900892

901893
return body
@@ -984,7 +976,6 @@ async def get_json(
984976
ignore_backoff: bool = False,
985977
try_trailing_slash_on_400: bool = False,
986978
parser: Literal[None] = None,
987-
max_response_size: Optional[int] = None,
988979
) -> Union[JsonDict, list]:
989980
...
990981

@@ -999,7 +990,6 @@ async def get_json(
999990
ignore_backoff: bool = ...,
1000991
try_trailing_slash_on_400: bool = ...,
1001992
parser: ByteParser[T] = ...,
1002-
max_response_size: Optional[int] = ...,
1003993
) -> T:
1004994
...
1005995

@@ -1013,7 +1003,6 @@ async def get_json(
10131003
ignore_backoff: bool = False,
10141004
try_trailing_slash_on_400: bool = False,
10151005
parser: Optional[ByteParser] = None,
1016-
max_response_size: Optional[int] = None,
10171006
):
10181007
"""GETs some json from the given host homeserver and path
10191008
@@ -1043,9 +1032,6 @@ async def get_json(
10431032
parser: The parser to use to decode the response. Defaults to
10441033
parsing as JSON.
10451034
1046-
max_response_size: The maximum size to read from the response. If None,
1047-
uses the default.
1048-
10491035
Returns:
10501036
Succeeds when we get a 2xx HTTP response. The
10511037
result will be the decoded JSON body.
@@ -1090,7 +1076,6 @@ async def get_json(
10901076
response,
10911077
start_ms,
10921078
parser=parser,
1093-
max_response_size=max_response_size,
10941079
)
10951080

10961081
return body

tests/http/test_fedclient.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
from synapse.api.errors import RequestSendFailed
2828
from synapse.http.matrixfederationclient import (
29-
MAX_RESPONSE_SIZE,
29+
JsonParser,
3030
MatrixFederationHttpClient,
3131
MatrixFederationRequest,
3232
)
@@ -609,9 +609,9 @@ def test_too_big(self):
609609
while not test_d.called:
610610
protocol.dataReceived(b"a" * chunk_size)
611611
sent += chunk_size
612-
self.assertLessEqual(sent, MAX_RESPONSE_SIZE)
612+
self.assertLessEqual(sent, JsonParser.MAX_RESPONSE_SIZE)
613613

614-
self.assertEqual(sent, MAX_RESPONSE_SIZE)
614+
self.assertEqual(sent, JsonParser.MAX_RESPONSE_SIZE)
615615

616616
f = self.failureResultOf(test_d)
617617
self.assertIsInstance(f.value, RequestSendFailed)

0 commit comments

Comments
 (0)