Fixed #5897 -- Added the Content-Length response header in CommonMiddleware

Thanks Tim Graham for the review.
This commit is contained in:
Claude Paroz 2016-06-18 10:51:38 +02:00
parent ca77b50905
commit 9588718cd4
6 changed files with 44 additions and 5 deletions

View File

@ -123,6 +123,10 @@ class CommonMiddleware(MiddlewareMixin):
etag=unquote_etag(response['ETag']), etag=unquote_etag(response['ETag']),
response=response, response=response,
) )
# Add the Content-Length header to non-streaming responses if not
# already set.
if not response.streaming and not response.has_header('Content-Length'):
response['Content-Length'] = str(len(response.content))
return response return response

View File

@ -66,6 +66,12 @@ Adds a few conveniences for perfectionists:
for each request by MD5-hashing the page content, and it'll take care of for each request by MD5-hashing the page content, and it'll take care of
sending ``Not Modified`` responses, if appropriate. sending ``Not Modified`` responses, if appropriate.
* Sets the ``Content-Length`` header for non-streaming responses.
.. versionchanged: 1.11
Older versions didn't set the ``Content-Length`` header.
.. attribute:: CommonMiddleware.response_redirect_class .. attribute:: CommonMiddleware.response_redirect_class
Defaults to :class:`~django.http.HttpResponsePermanentRedirect`. Subclass Defaults to :class:`~django.http.HttpResponsePermanentRedirect`. Subclass

View File

@ -197,6 +197,9 @@ Requests and Responses
* Added :meth:`QueryDict.fromkeys() <django.http.QueryDict.fromkeys>`. * Added :meth:`QueryDict.fromkeys() <django.http.QueryDict.fromkeys>`.
* :class:`~django.middleware.common.CommonMiddleware` now sets the
``Content-Length`` response header for non-streaming responses.
Serialization Serialization
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View File

@ -285,6 +285,27 @@ class CommonMiddlewareTest(SimpleTestCase):
second_res = CommonMiddleware().process_response(second_req, HttpResponse('content')) second_res = CommonMiddleware().process_response(second_req, HttpResponse('content'))
self.assertEqual(second_res.status_code, 304) self.assertEqual(second_res.status_code, 304)
# Tests for the Content-Length header
def test_content_length_header_added(self):
response = HttpResponse('content')
self.assertNotIn('Content-Length', response)
response = CommonMiddleware().process_response(HttpRequest(), response)
self.assertEqual(int(response['Content-Length']), len(response.content))
def test_content_length_header_not_added_for_streaming_response(self):
response = StreamingHttpResponse('content')
self.assertNotIn('Content-Length', response)
response = CommonMiddleware().process_response(HttpRequest(), response)
self.assertNotIn('Content-Length', response)
def test_content_length_header_not_changed(self):
response = HttpResponse()
bad_content_length = len(response.content) + 10
response['Content-Length'] = bad_content_length
response = CommonMiddleware().process_response(HttpRequest(), response)
self.assertEqual(int(response['Content-Length']), bad_content_length)
# Other tests # Other tests
@override_settings(DISALLOWED_USER_AGENTS=[re.compile(r'foo')]) @override_settings(DISALLOWED_USER_AGENTS=[re.compile(r'foo')])
@ -445,6 +466,9 @@ class ConditionalGetMiddlewareTest(SimpleTestCase):
def test_content_length_header_added(self): def test_content_length_header_added(self):
content_length = len(self.resp.content) content_length = len(self.resp.content)
# Already set by CommonMiddleware, remove it to check that
# ConditionalGetMiddleware readds it.
del self.resp['Content-Length']
self.assertNotIn('Content-Length', self.resp) self.assertNotIn('Content-Length', self.resp)
self.resp = ConditionalGetMiddleware().process_response(self.req, self.resp) self.resp = ConditionalGetMiddleware().process_response(self.req, self.resp)
self.assertIn('Content-Length', self.resp) self.assertIn('Content-Length', self.resp)

View File

@ -41,6 +41,7 @@ class TestStartProjectSettings(TestCase):
response = self.client.get('/empty/') response = self.client.get('/empty/')
headers = sorted(response.serialize_headers().split(b'\r\n')) headers = sorted(response.serialize_headers().split(b'\r\n'))
self.assertEqual(headers, [ self.assertEqual(headers, [
b'Content-Length: 0',
b'Content-Type: text/html; charset=utf-8', b'Content-Type: text/html; charset=utf-8',
b'X-Frame-Options: SAMEORIGIN', b'X-Frame-Options: SAMEORIGIN',
]) ])

View File

@ -41,11 +41,12 @@ class WSGITest(SimpleTestCase):
self.assertEqual(response_data["status"], "200 OK") self.assertEqual(response_data["status"], "200 OK")
self.assertEqual( self.assertEqual(
response_data["headers"], set(response_data["headers"]),
[('Content-Type', 'text/html; charset=utf-8')]) {('Content-Length', '12'), ('Content-Type', 'text/html; charset=utf-8')})
self.assertEqual( self.assertTrue(bytes(response) in [
bytes(response), b"Content-Length: 12\r\nContent-Type: text/html; charset=utf-8\r\n\r\nHello World!",
b"Content-Type: text/html; charset=utf-8\r\n\r\nHello World!") b"Content-Type: text/html; charset=utf-8\r\nContent-Length: 12\r\n\r\nHello World!"
])
def test_file_wrapper(self): def test_file_wrapper(self):
""" """