summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Christopher Henry <k@severian.com>2016-10-11 03:42:00 -0400
committerTim Graham <timograham@gmail.com>2016-10-13 14:22:54 -0400
commitad332e5ca9503bba2e14cc8c3ca675eed56d72bc (patch)
tree4cf3ea2b0c921d10cfe001702227b9eb86bf6f57
parent816eae35084a69a76e45439df20f69c7e0ccaef9 (diff)
Refs #19705 -- Made GZipMiddleware make ETags weak.
Django's conditional request processing can now produce 304 Not Modified responses for content that is subject to compression.
-rw-r--r--django/middleware/gzip.py8
-rw-r--r--docs/ref/middleware.txt3
-rw-r--r--tests/middleware/tests.py43
3 files changed, 39 insertions, 15 deletions
diff --git a/django/middleware/gzip.py b/django/middleware/gzip.py
index 642e3188d3..d6e2134f17 100644
--- a/django/middleware/gzip.py
+++ b/django/middleware/gzip.py
@@ -41,8 +41,12 @@ class GZipMiddleware(MiddlewareMixin):
response.content = compressed_content
response['Content-Length'] = str(len(response.content))
- if response.has_header('ETag'):
- response['ETag'] = re.sub('"$', ';gzip"', response['ETag'])
+ # If there is a strong ETag, make it weak to fulfill the requirements
+ # of RFC 7232 section-2.1 while also allowing conditional request
+ # matches on ETags.
+ etag = response.get('ETag')
+ if etag and etag.startswith('"'):
+ response['ETag'] = 'W/' + etag
response['Content-Encoding'] = 'gzip'
return response
diff --git a/docs/ref/middleware.txt b/docs/ref/middleware.txt
index 432f5437ff..d007d252f5 100644
--- a/docs/ref/middleware.txt
+++ b/docs/ref/middleware.txt
@@ -155,6 +155,9 @@ It will NOT compress content if any of the following are true:
* The request (the browser) hasn't sent an ``Accept-Encoding`` header
containing ``gzip``.
+If the response has an ``ETag`` header, the ETag is made weak to comply with
+:rfc:`7232#section-2.1`.
+
You can apply GZip compression to individual views using the
:func:`~django.views.decorators.gzip.gzip_page()` decorator.
diff --git a/tests/middleware/tests.py b/tests/middleware/tests.py
index a9cb1f31ea..db8ed6ea90 100644
--- a/tests/middleware/tests.py
+++ b/tests/middleware/tests.py
@@ -855,27 +855,44 @@ class GZipMiddlewareTest(SimpleTestCase):
@override_settings(USE_ETAGS=True)
class ETagGZipMiddlewareTest(SimpleTestCase):
"""
- Tests if the ETagMiddleware behaves correctly with GZipMiddleware.
+ ETags are handled properly by GZipMiddleware.
"""
rf = RequestFactory()
compressible_string = b'a' * 500
- def test_compress_response(self):
+ def test_strong_etag_modified(self):
"""
- ETag is changed after gzip compression is performed.
+ GZipMiddleware makes a strong ETag weak.
"""
request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
- response = GZipMiddleware().process_response(
- request,
- CommonMiddleware().process_response(request, HttpResponse(self.compressible_string))
- )
- gzip_etag = response.get('ETag')
+ response = HttpResponse(self.compressible_string)
+ response['ETag'] = '"eggs"'
+ gzip_response = GZipMiddleware().process_response(request, response)
+ self.assertEqual(gzip_response['ETag'], 'W/"eggs"')
- request = self.rf.get('/', HTTP_ACCEPT_ENCODING='')
+ def test_weak_etag_not_modified(self):
+ """
+ GZipMiddleware doesn't modify a weak ETag.
+ """
+ request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
+ response = HttpResponse(self.compressible_string)
+ response['ETag'] = 'W/"eggs"'
+ gzip_response = GZipMiddleware().process_response(request, response)
+ self.assertEqual(gzip_response['ETag'], 'W/"eggs"')
+
+ def test_etag_match(self):
+ """
+ GZipMiddleware allows 304 Not Modified responses.
+ """
+ request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate')
response = GZipMiddleware().process_response(
request,
- CommonMiddleware().process_response(request, HttpResponse(self.compressible_string))
+ ConditionalGetMiddleware().process_response(request, HttpResponse(self.compressible_string))
)
- nogzip_etag = response.get('ETag')
-
- self.assertNotEqual(gzip_etag, nogzip_etag)
+ gzip_etag = response['ETag']
+ next_request = self.rf.get('/', HTTP_ACCEPT_ENCODING='gzip, deflate', HTTP_IF_NONE_MATCH=gzip_etag)
+ next_response = ConditionalGetMiddleware().process_response(
+ next_request,
+ HttpResponse(self.compressible_string)
+ )
+ self.assertEqual(next_response.status_code, 304)