summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2012-10-20 14:33:57 +0200
committerClaude Paroz <claude@2xlibre.net>2012-10-20 14:56:16 +0200
commitdfd4a7175119ddb422d8426dcc15902265d5a428 (patch)
tree401b547f104a9d45a098d61f40b901b6eea71243
parenteed4faf16f37a8b0af06a52eada05b84dead4c0d (diff)
Fixed #5611 -- Restricted accepted content types in parsing POST data
Thanks paulegan for the report and Preston Holmes for the review.
-rw-r--r--django/http/__init__.py8
-rw-r--r--docs/ref/request-response.txt10
-rw-r--r--docs/releases/1.5.txt12
-rw-r--r--tests/regressiontests/requests/tests.py25
4 files changed, 50 insertions, 5 deletions
diff --git a/django/http/__init__.py b/django/http/__init__.py
index b385b450ee..b67c182c37 100644
--- a/django/http/__init__.py
+++ b/django/http/__init__.py
@@ -315,7 +315,7 @@ class HttpRequest(object):
self._post_parse_error = True
def _load_post_and_files(self):
- # Populates self._post and self._files
+ """Populate self._post and self._files if the content-type is a form type"""
if self.method != 'POST':
self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
return
@@ -323,7 +323,7 @@ class HttpRequest(object):
self._mark_post_parse_error()
return
- if self.META.get('CONTENT_TYPE', '').startswith('multipart'):
+ if self.META.get('CONTENT_TYPE', '').startswith('multipart/form-data'):
if hasattr(self, '_body'):
# Use already read data
data = BytesIO(self._body)
@@ -341,8 +341,10 @@ class HttpRequest(object):
# empty POST
self._mark_post_parse_error()
raise
- else:
+ elif self.META.get('CONTENT_TYPE', '').startswith('application/x-www-form-urlencoded'):
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
+ else:
+ self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
## File-like and iterator interface.
##
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 0a337eba42..d7266f0aff 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -92,8 +92,14 @@ All attributes should be considered read-only, unless stated otherwise below.
.. attribute:: HttpRequest.POST
- A dictionary-like object containing all given HTTP POST parameters. See the
- :class:`QueryDict` documentation below.
+ A dictionary-like object containing all given HTTP POST parameters,
+ providing that the request contains form data. See the
+ :class:`QueryDict` documentation below. If you need to access raw or
+ non-form data posted in the request, access this through the
+ :attr:`HttpRequest.body` attribute instead.
+
+ .. versionchanged:: 1.5
+ Before Django 1.5, HttpRequest.POST contained non-form data.
It's possible that a request can come in via POST with an empty ``POST``
dictionary -- if, say, a form is requested via the POST HTTP method but
diff --git a/docs/releases/1.5.txt b/docs/releases/1.5.txt
index d49bae801d..d30bd5ff7e 100644
--- a/docs/releases/1.5.txt
+++ b/docs/releases/1.5.txt
@@ -245,6 +245,18 @@ For consistency with the design of the other generic views,
dictionary into the context, instead passing the variables from the URLconf
directly into the context.
+Non-form data in HTTP requests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:attr:`request.POST <django.http.HttpRequest.POST>` will no longer include data
+posted via HTTP requests with non form-specific content-types in the header.
+In prior versions, data posted with content-types other than
+``multipart/form-data`` or ``application/x-www-form-urlencoded`` would still
+end up represented in the :attr:`request.POST <django.http.HttpRequest.POST>`
+attribute. Developers wishing to access the raw POST data for these cases,
+should use the :attr:`request.body <django.http.HttpRequest.body>` attribute
+instead.
+
OPTIONS, PUT and DELETE requests in the test client
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py
index d80161371e..378b4cf6d9 100644
--- a/tests/regressiontests/requests/tests.py
+++ b/tests/regressiontests/requests/tests.py
@@ -330,6 +330,7 @@ class RequestsTests(unittest.TestCase):
def test_stream(self):
payload = b'name=value'
request = WSGIRequest({'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(payload),
'wsgi.input': BytesIO(payload)})
self.assertEqual(request.read(), b'name=value')
@@ -341,6 +342,7 @@ class RequestsTests(unittest.TestCase):
"""
payload = b'name=value'
request = WSGIRequest({'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(payload),
'wsgi.input': BytesIO(payload)})
self.assertEqual(request.POST, {'name': ['value']})
@@ -354,6 +356,7 @@ class RequestsTests(unittest.TestCase):
"""
payload = b'name=value'
request = WSGIRequest({'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(payload),
'wsgi.input': BytesIO(payload)})
self.assertEqual(request.read(2), b'na')
@@ -402,9 +405,28 @@ class RequestsTests(unittest.TestCase):
'wsgi.input': BytesIO(payload)})
self.assertEqual(request.POST, {})
+ def test_POST_binary_only(self):
+ payload = b'\r\n\x01\x00\x00\x00ab\x00\x00\xcd\xcc,@'
+ environ = {'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/octet-stream',
+ 'CONTENT_LENGTH': len(payload),
+ 'wsgi.input': BytesIO(payload)}
+ request = WSGIRequest(environ)
+ self.assertEqual(request.POST, {})
+ self.assertEqual(request.FILES, {})
+ self.assertEqual(request.body, payload)
+
+ # Same test without specifying content-type
+ environ.update({'CONTENT_TYPE': '', 'wsgi.input': BytesIO(payload)})
+ request = WSGIRequest(environ)
+ self.assertEqual(request.POST, {})
+ self.assertEqual(request.FILES, {})
+ self.assertEqual(request.body, payload)
+
def test_read_by_lines(self):
payload = b'name=value'
request = WSGIRequest({'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(payload),
'wsgi.input': BytesIO(payload)})
self.assertEqual(list(request), [b'name=value'])
@@ -415,6 +437,7 @@ class RequestsTests(unittest.TestCase):
"""
payload = b'name=value'
request = WSGIRequest({'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(payload),
'wsgi.input': BytesIO(payload)})
raw_data = request.body
@@ -427,6 +450,7 @@ class RequestsTests(unittest.TestCase):
"""
payload = b'name=value'
request = WSGIRequest({'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(payload),
'wsgi.input': BytesIO(payload)})
raw_data = request.body
@@ -479,6 +503,7 @@ class RequestsTests(unittest.TestCase):
payload = b'name=value'
request = WSGIRequest({'REQUEST_METHOD': 'POST',
+ 'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': len(payload),
'wsgi.input': ExplodingBytesIO(payload)})