Limit the size of the request body
This commit is contained in:
@@ -189,6 +189,15 @@ class Request():
|
||||
:var g: A general purpose container for applications to store data during
|
||||
the life of the request.
|
||||
"""
|
||||
#: Specify the maximum payload size that is accepted. Requests with larger
|
||||
#: payloads will be rejected with a 413 status code. Applications can
|
||||
#: change this maximum as necessary.
|
||||
#:
|
||||
#: Example::
|
||||
#:
|
||||
#: Request.max_content_length = 1 * 1024 * 1024 # 1MB requests allowed
|
||||
max_content_length = 16 * 1024
|
||||
|
||||
class G:
|
||||
pass
|
||||
|
||||
@@ -255,7 +264,8 @@ class Request():
|
||||
content_length = int(value)
|
||||
|
||||
# body
|
||||
body = client_stream.read(content_length) if content_length else b''
|
||||
body = client_stream.read(content_length) if content_length and \
|
||||
content_length <= Request.max_content_length else b''
|
||||
|
||||
return Request(app, client_addr, method, url, http_version, headers,
|
||||
body)
|
||||
@@ -770,39 +780,45 @@ class Microdot():
|
||||
|
||||
req = Request.create(self, stream, addr)
|
||||
if req:
|
||||
f = self.find_route(req)
|
||||
try:
|
||||
res = None
|
||||
if f:
|
||||
for handler in self.before_request_handlers:
|
||||
res = handler(req)
|
||||
if res:
|
||||
break
|
||||
if res is None:
|
||||
res = f(req, **req.url_args)
|
||||
if isinstance(res, tuple):
|
||||
res = Response(*res)
|
||||
elif not isinstance(res, Response):
|
||||
res = Response(res)
|
||||
for handler in self.after_request_handlers:
|
||||
res = handler(req, res) or res
|
||||
elif 404 in self.error_handlers:
|
||||
res = self.error_handlers[404](req)
|
||||
if req.content_length > req.max_content_length:
|
||||
if 413 in self.error_handlers:
|
||||
res = self.error_handlers[413](req)
|
||||
else:
|
||||
res = 'Not found', 404
|
||||
except Exception as exc:
|
||||
print_exception(exc)
|
||||
res = None
|
||||
if exc.__class__ in self.error_handlers:
|
||||
try:
|
||||
res = self.error_handlers[exc.__class__](req, exc)
|
||||
except Exception as exc2: # pragma: no cover
|
||||
print_exception(exc2)
|
||||
if res is None:
|
||||
if 500 in self.error_handlers:
|
||||
res = self.error_handlers[500](req)
|
||||
res = 'Payload too large', 413
|
||||
else:
|
||||
f = self.find_route(req)
|
||||
try:
|
||||
res = None
|
||||
if f:
|
||||
for handler in self.before_request_handlers:
|
||||
res = handler(req)
|
||||
if res:
|
||||
break
|
||||
if res is None:
|
||||
res = f(req, **req.url_args)
|
||||
if isinstance(res, tuple):
|
||||
res = Response(*res)
|
||||
elif not isinstance(res, Response):
|
||||
res = Response(res)
|
||||
for handler in self.after_request_handlers:
|
||||
res = handler(req, res) or res
|
||||
elif 404 in self.error_handlers:
|
||||
res = self.error_handlers[404](req)
|
||||
else:
|
||||
res = 'Internal server error', 500
|
||||
res = 'Not found', 404
|
||||
except Exception as exc:
|
||||
print_exception(exc)
|
||||
res = None
|
||||
if exc.__class__ in self.error_handlers:
|
||||
try:
|
||||
res = self.error_handlers[exc.__class__](req, exc)
|
||||
except Exception as exc2: # pragma: no cover
|
||||
print_exception(exc2)
|
||||
if res is None:
|
||||
if 500 in self.error_handlers:
|
||||
res = self.error_handlers[500](req)
|
||||
else:
|
||||
res = 'Internal server error', 500
|
||||
if isinstance(res, tuple):
|
||||
res = Response(*res)
|
||||
elif not isinstance(res, Response):
|
||||
|
||||
@@ -54,8 +54,8 @@ class Request(BaseRequest):
|
||||
content_length = int(value)
|
||||
|
||||
# body
|
||||
body = await client_stream.read(content_length) \
|
||||
if content_length else b''
|
||||
body = await client_stream.read(content_length) if content_length and \
|
||||
content_length <= Request.max_content_length else b''
|
||||
|
||||
return Request(app, client_addr, method, url, http_version, headers,
|
||||
body)
|
||||
@@ -213,44 +213,51 @@ class Microdot(BaseMicrodot):
|
||||
req = await Request.create(self, reader,
|
||||
writer.get_extra_info('peername'))
|
||||
if req:
|
||||
f = self.find_route(req)
|
||||
try:
|
||||
res = None
|
||||
if f:
|
||||
for handler in self.before_request_handlers:
|
||||
res = await self._invoke_handler(handler, req)
|
||||
if res:
|
||||
break
|
||||
if res is None:
|
||||
res = await self._invoke_handler(
|
||||
f, req, **req.url_args)
|
||||
if isinstance(res, tuple):
|
||||
res = Response(*res)
|
||||
elif not isinstance(res, Response):
|
||||
res = Response(res)
|
||||
for handler in self.after_request_handlers:
|
||||
res = await self._invoke_handler(
|
||||
handler, req, res) or res
|
||||
elif 404 in self.error_handlers:
|
||||
if req.content_length > req.max_content_length:
|
||||
if 413 in self.error_handlers:
|
||||
res = await self._invoke_handler(
|
||||
self.error_handlers[404], req)
|
||||
self.error_handlers[413], req)
|
||||
else:
|
||||
res = 'Not found', 404
|
||||
except Exception as exc:
|
||||
print_exception(exc)
|
||||
res = None
|
||||
if exc.__class__ in self.error_handlers:
|
||||
try:
|
||||
res = 'Payload too large', 413
|
||||
else:
|
||||
f = self.find_route(req)
|
||||
try:
|
||||
res = None
|
||||
if f:
|
||||
for handler in self.before_request_handlers:
|
||||
res = await self._invoke_handler(handler, req)
|
||||
if res:
|
||||
break
|
||||
if res is None:
|
||||
res = await self._invoke_handler(
|
||||
f, req, **req.url_args)
|
||||
if isinstance(res, tuple):
|
||||
res = Response(*res)
|
||||
elif not isinstance(res, Response):
|
||||
res = Response(res)
|
||||
for handler in self.after_request_handlers:
|
||||
res = await self._invoke_handler(
|
||||
handler, req, res) or res
|
||||
elif 404 in self.error_handlers:
|
||||
res = await self._invoke_handler(
|
||||
self.error_handlers[exc.__class__], req, exc)
|
||||
except Exception as exc2: # pragma: no cover
|
||||
print_exception(exc2)
|
||||
if res is None:
|
||||
if 500 in self.error_handlers:
|
||||
res = await self._invoke_handler(
|
||||
self.error_handlers[500], req)
|
||||
self.error_handlers[404], req)
|
||||
else:
|
||||
res = 'Internal server error', 500
|
||||
res = 'Not found', 404
|
||||
except Exception as exc:
|
||||
print_exception(exc)
|
||||
res = None
|
||||
if exc.__class__ in self.error_handlers:
|
||||
try:
|
||||
res = await self._invoke_handler(
|
||||
self.error_handlers[exc.__class__], req, exc)
|
||||
except Exception as exc2: # pragma: no cover
|
||||
print_exception(exc2)
|
||||
if res is None:
|
||||
if 500 in self.error_handlers:
|
||||
res = await self._invoke_handler(
|
||||
self.error_handlers[500], req)
|
||||
else:
|
||||
res = 'Internal server error', 500
|
||||
if isinstance(res, tuple):
|
||||
res = Response(*res)
|
||||
elif not isinstance(res, Response):
|
||||
|
||||
@@ -192,6 +192,42 @@ class TestMicrodot(unittest.TestCase):
|
||||
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||
self.assertTrue(fd.response.endswith(b'\r\n\r\n404'))
|
||||
|
||||
def test_413(self):
|
||||
app = Microdot()
|
||||
|
||||
@app.route('/')
|
||||
def index(req):
|
||||
return 'foo'
|
||||
|
||||
mock_socket.clear_requests()
|
||||
fd = mock_socket.add_request('GET', '/foo', body='x' * 17000)
|
||||
self._add_shutdown(app)
|
||||
app.run()
|
||||
self.assertTrue(fd.response.startswith(b'HTTP/1.0 413 N/A\r\n'))
|
||||
self.assertIn(b'Content-Length: 17\r\n', fd.response)
|
||||
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||
self.assertTrue(fd.response.endswith(b'\r\n\r\nPayload too large'))
|
||||
|
||||
def test_413_handler(self):
|
||||
app = Microdot()
|
||||
|
||||
@app.route('/')
|
||||
def index(req):
|
||||
return 'foo'
|
||||
|
||||
@app.errorhandler(413)
|
||||
def handle_413(req):
|
||||
return '413', 400
|
||||
|
||||
mock_socket.clear_requests()
|
||||
fd = mock_socket.add_request('GET', '/foo', body='x' * 17000)
|
||||
self._add_shutdown(app)
|
||||
app.run()
|
||||
self.assertTrue(fd.response.startswith(b'HTTP/1.0 400 N/A\r\n'))
|
||||
self.assertIn(b'Content-Length: 3\r\n', fd.response)
|
||||
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||
self.assertTrue(fd.response.endswith(b'\r\n\r\n413'))
|
||||
|
||||
def test_500(self):
|
||||
app = Microdot()
|
||||
|
||||
|
||||
@@ -78,3 +78,15 @@ class TestRequest(unittest.TestCase):
|
||||
body='foo=bar&abc=def&x=%2f%%')
|
||||
req = Request.create('app', fd, 'addr')
|
||||
self.assertIsNone(req.form)
|
||||
|
||||
def test_large_payload(self):
|
||||
saved_max_content_length = Request.max_content_length
|
||||
Request.max_content_length = 16
|
||||
|
||||
fd = get_request_fd('GET', '/foo', headers={
|
||||
'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body='foo=bar&abc=def&x=y')
|
||||
req = Request.create('app', fd, 'addr')
|
||||
assert req.body == b''
|
||||
|
||||
Request.max_content_length = saved_max_content_length
|
||||
|
||||
@@ -170,6 +170,42 @@ class TestMicrodotAsync(unittest.TestCase):
|
||||
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||
self.assertTrue(fd.response.endswith(b'\r\n\r\n404'))
|
||||
|
||||
def test_413(self):
|
||||
app = Microdot()
|
||||
|
||||
@app.route('/')
|
||||
def index(req):
|
||||
return 'foo'
|
||||
|
||||
mock_socket.clear_requests()
|
||||
fd = mock_socket.add_request('GET', '/foo', body='x' * 17000)
|
||||
self._add_shutdown(app)
|
||||
app.run()
|
||||
self.assertTrue(fd.response.startswith(b'HTTP/1.0 413 N/A\r\n'))
|
||||
self.assertIn(b'Content-Length: 17\r\n', fd.response)
|
||||
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||
self.assertTrue(fd.response.endswith(b'\r\n\r\nPayload too large'))
|
||||
|
||||
def test_413_handler(self):
|
||||
app = Microdot()
|
||||
|
||||
@app.route('/')
|
||||
def index(req):
|
||||
return 'foo'
|
||||
|
||||
@app.errorhandler(413)
|
||||
async def handle_413(req):
|
||||
return '413', 400
|
||||
|
||||
mock_socket.clear_requests()
|
||||
fd = mock_socket.add_request('GET', '/foo', body='x' * 17000)
|
||||
self._add_shutdown(app)
|
||||
app.run()
|
||||
self.assertTrue(fd.response.startswith(b'HTTP/1.0 400 N/A\r\n'))
|
||||
self.assertIn(b'Content-Length: 3\r\n', fd.response)
|
||||
self.assertIn(b'Content-Type: text/plain\r\n', fd.response)
|
||||
self.assertTrue(fd.response.endswith(b'\r\n\r\n413'))
|
||||
|
||||
def test_500(self):
|
||||
app = Microdot()
|
||||
|
||||
|
||||
@@ -88,3 +88,15 @@ class TestRequestAsync(unittest.TestCase):
|
||||
body='foo=bar&abc=def&x=%2f%%')
|
||||
req = _run(Request.create('app', fd, 'addr'))
|
||||
self.assertIsNone(req.form)
|
||||
|
||||
def test_large_payload(self):
|
||||
saved_max_content_length = Request.max_content_length
|
||||
Request.max_content_length = 16
|
||||
|
||||
fd = get_async_request_fd('GET', '/foo', headers={
|
||||
'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body='foo=bar&abc=def&x=y')
|
||||
req = _run(Request.create('app', fd, 'addr'))
|
||||
assert req.body == b''
|
||||
|
||||
Request.max_content_length = saved_max_content_length
|
||||
|
||||
Reference in New Issue
Block a user