Commit 8bb46d8b authored by Luke Plant's avatar Luke Plant
Browse files

Fixed #15679 - regression in HttpRequest.POST and raw_post_data access.

Thanks to vkryachko for the report.

This also fixes a slight inconsistency with raw_post_data after parsing of a
multipart request, and adds a test for that.  (Previously accessing
raw_post_data would have returned the empty string rather than raising an
Exception).

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15938 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent 728770a5
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -262,14 +262,18 @@ class HttpRequest(object):
        if self.method != 'POST':
            self._post, self._files = QueryDict('', encoding=self._encoding), MultiValueDict()
            return
        if self._read_started:
        if self._read_started and not hasattr(self, '_raw_post_data'):
            self._mark_post_parse_error()
            return

        if self.META.get('CONTENT_TYPE', '').startswith('multipart'):
            self._raw_post_data = ''
            if hasattr(self, '_raw_post_data'):
                # Use already read data
                data = StringIO(self._raw_post_data)
            else:
                data = self
            try:
                self._post, self._files = self.parse_file_upload(self.META, self)
                self._post, self._files = self.parse_file_upload(self.META, data)
            except:
                # An error occured while parsing POST data.  Since when
                # formatting the error the request handler might access
+60 −0
Original line number Diff line number Diff line
@@ -179,6 +179,66 @@ class RequestsTests(unittest.TestCase):
        self.assertRaises(Exception, lambda: request.raw_post_data)
        self.assertEqual(request.POST, {})

    def test_raw_post_data_after_POST_multipart(self):
        """
        Reading raw_post_data after parsing multipart is not allowed
        """
        # Because multipart is used for large amounts fo data i.e. file uploads,
        # we don't want the data held in memory twice, and we don't want to
        # silence the error by setting raw_post_data = '' either.
        payload = "\r\n".join([
                '--boundary',
                'Content-Disposition: form-data; name="name"',
                '',
                'value',
                '--boundary--'
                ''])
        request = WSGIRequest({'REQUEST_METHOD': 'POST',
                               'CONTENT_TYPE': 'multipart/form-data; boundary=boundary',
                               'CONTENT_LENGTH': len(payload),
                               'wsgi.input': StringIO(payload)})
        self.assertEqual(request.POST, {u'name': [u'value']})
        self.assertRaises(Exception, lambda: request.raw_post_data)

    def test_read_by_lines(self):
        request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')})
        self.assertEqual(list(request), ['name=value'])

    def test_POST_after_raw_post_data_read(self):
        """
        POST should be populated even if raw_post_data is read first
        """
        request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')})
        raw_data = request.raw_post_data
        self.assertEqual(request.POST, {u'name': [u'value']})

    def test_POST_after_raw_post_data_read_and_stream_read(self):
        """
        POST should be populated even if raw_post_data is read first, and then
        the stream is read second.
        """
        request = WSGIRequest({'REQUEST_METHOD': 'POST', 'wsgi.input': StringIO('name=value')})
        raw_data = request.raw_post_data
        self.assertEqual(request.read(1), u'n')
        self.assertEqual(request.POST, {u'name': [u'value']})

    def test_POST_after_raw_post_data_read_and_stream_read_multipart(self):
        """
        POST should be populated even if raw_post_data is read first, and then
        the stream is read second. Using multipart/form-data instead of urlencoded.
        """
        payload = "\r\n".join([
                '--boundary',
                'Content-Disposition: form-data; name="name"',
                '',
                'value',
                '--boundary--'
                ''])
        request = WSGIRequest({'REQUEST_METHOD': 'POST',
                               'CONTENT_TYPE': 'multipart/form-data; boundary=boundary',
                               'CONTENT_LENGTH': len(payload),
                               'wsgi.input': StringIO(payload)})
        raw_data = request.raw_post_data
        # Consume enough data to mess up the parsing:
        self.assertEqual(request.read(13), u'--boundary\r\nC')
        self.assertEqual(request.POST, {u'name': [u'value']})