1414import contextlib
1515import logging
1616import time
17- from typing import Optional , Tuple , Type , Union
17+ from typing import Optional , Tuple , Union
1818
1919import attr
2020from zope .interface import implementer
@@ -50,6 +50,7 @@ class SynapseRequest(Request):
5050 * Redaction of access_token query-params in __repr__
5151 * Logging at start and end
5252 * Metrics to record CPU, wallclock and DB time by endpoint.
53+ * A limit to the size of request which will be accepted
5354
5455 It also provides a method `processing`, which returns a context manager. If this
5556 method is called, the request won't be logged until the context manager is closed;
@@ -60,8 +61,9 @@ class SynapseRequest(Request):
6061 logcontext: the log context for this request
6162 """
6263
63- def __init__ (self , channel , * args , ** kw ):
64+ def __init__ (self , channel , * args , max_request_body_size = 1024 , ** kw ):
6465 Request .__init__ (self , channel , * args , ** kw )
66+ self ._max_request_body_size = max_request_body_size
6567 self .site = channel .site # type: SynapseSite
6668 self ._channel = channel # this is used by the tests
6769 self .start_time = 0.0
@@ -98,6 +100,18 @@ def __repr__(self):
98100 self .site .site_tag ,
99101 )
100102
103+ def handleContentChunk (self , data ):
104+ # we should have a `content` by now.
105+ assert self .content , "handleContentChunk() called before gotLength()"
106+ if self .content .tell () + len (data ) > self ._max_request_body_size :
107+ logger .warning (
108+ "Aborting connection from %s because the request exceeds maximum size" ,
109+ self .client ,
110+ )
111+ self .transport .abortConnection ()
112+ return
113+ super ().handleContentChunk (data )
114+
101115 @property
102116 def requester (self ) -> Optional [Union [Requester , str ]]:
103117 return self ._requester
@@ -505,6 +519,7 @@ def __init__(
505519 config : ListenerConfig ,
506520 resource : IResource ,
507521 server_version_string ,
522+ max_request_body_size : int ,
508523 reactor : IReactorTime ,
509524 ):
510525 """
@@ -516,6 +531,8 @@ def __init__(
516531 resource: The base of the resource tree to be used for serving requests on
517532 this site
518533 server_version_string: A string to present for the Server header
534+ max_request_body_size: Maximum request body length to allow before
535+ dropping the connection
519536 reactor: reactor to be used to manage connection timeouts
520537 """
521538 Site .__init__ (self , resource , reactor = reactor )
@@ -524,9 +541,14 @@ def __init__(
524541
525542 assert config .http_options is not None
526543 proxied = config .http_options .x_forwarded
527- self .requestFactory = (
528- XForwardedForRequest if proxied else SynapseRequest
529- ) # type: Type[Request]
544+ request_class = XForwardedForRequest if proxied else SynapseRequest
545+
546+ def request_factory (channel , queued ) -> Request :
547+ return request_class (
548+ channel , max_request_body_size = max_request_body_size , queued = queued
549+ )
550+
551+ self .requestFactory = request_factory # type: ignore
530552 self .access_logger = logging .getLogger (logger_name )
531553 self .server_version_string = server_version_string .encode ("ascii" )
532554
0 commit comments