summaryrefslogtreecommitdiff
path: root/django/http/request.py
diff options
context:
space:
mode:
Diffstat (limited to 'django/http/request.py')
-rw-r--r--django/http/request.py45
1 files changed, 35 insertions, 10 deletions
diff --git a/django/http/request.py b/django/http/request.py
index 4d1d077ec1..0b86444c4b 100644
--- a/django/http/request.py
+++ b/django/http/request.py
@@ -1,5 +1,6 @@
import codecs
import copy
+import os
from io import BytesIO
from itertools import chain
from urllib.parse import parse_qsl, quote, urlencode, urljoin, urlsplit
@@ -328,25 +329,49 @@ class HttpRequest:
"You cannot access body after reading from request's data stream"
)
- # Limit the maximum request data size that will be handled in-memory.
- if (
- settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None
- and int(self.META.get("CONTENT_LENGTH") or 0)
- > settings.DATA_UPLOAD_MAX_MEMORY_SIZE
- ):
- raise RequestDataTooBig(
- "Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE."
- )
+ # Limit the maximum request data size that will be handled
+ # in-memory. Reject early when Content-Length is present and
+ # already exceeds the limit, avoiding reading the body at all.
+ self._check_data_too_big(int(self.META.get("CONTENT_LENGTH") or 0))
+
+ # Content-Length can be absent or understated (e.g.
+ # `Transfer-Encoding: chunked` on ASGI), so for seekable
+ # streams (e.g. SpooledTemporaryFile on ASGI), check the actual
+ # buffered size before reading it all into memory.
+ if hasattr(self._stream, "seekable") and self._stream.seekable():
+ stream_size = self._stream.seek(0, os.SEEK_END)
+ self._check_data_too_big(stream_size)
+ self._stream.seek(0)
+ did_check = True
+ else:
+ did_check = False
try:
- self._body = self.read()
+ if settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None and not did_check:
+ # Read one byte past the limit to detect an oversize body
+ # without loading it all into memory first.
+ self._body = self.read(settings.DATA_UPLOAD_MAX_MEMORY_SIZE + 1)
+ else:
+ self._body = self.read()
except OSError as e:
raise UnreadablePostError(*e.args) from e
finally:
self._stream.close()
self._stream = BytesIO(self._body)
+ if not did_check:
+ stream_size = self._stream.seek(0, os.SEEK_END)
+ self._check_data_too_big(stream_size)
+ self._stream.seek(0)
return self._body
+ def _check_data_too_big(self, length):
+ if (
+ settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None
+ and length > settings.DATA_UPLOAD_MAX_MEMORY_SIZE
+ ):
+ msg = "Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE."
+ raise RequestDataTooBig(msg)
+
def _mark_post_parse_error(self):
self._post = QueryDict()
self._files = MultiValueDict()