Refs #19705 -- Changed gzip modification times to 0.
This makes gzip output deterministic, which allows ConditionalGetMiddleware to reliably compare ETags on gzipped content (views using the gzip_page() decorator in particular).
This commit is contained in:
parent
9eb49af821
commit
9108696a75
|
@ -293,7 +293,7 @@ def phone2numeric(phone):
|
|||
# Used with permission.
|
||||
def compress_string(s):
|
||||
zbuf = BytesIO()
|
||||
with GzipFile(mode='wb', compresslevel=6, fileobj=zbuf) as zfile:
|
||||
with GzipFile(mode='wb', compresslevel=6, fileobj=zbuf, mtime=0) as zfile:
|
||||
zfile.write(s)
|
||||
return zbuf.getvalue()
|
||||
|
||||
|
@ -322,7 +322,7 @@ class StreamingBuffer(object):
|
|||
# Like compress_string, but for iterators of strings.
|
||||
def compress_sequence(sequence):
|
||||
buf = StreamingBuffer()
|
||||
with GzipFile(mode='wb', compresslevel=6, fileobj=buf) as zfile:
|
||||
with GzipFile(mode='wb', compresslevel=6, fileobj=buf, mtime=0) as zfile:
|
||||
# Output headers...
|
||||
yield buf.read()
|
||||
for item in sequence:
|
||||
|
|
|
@ -770,6 +770,12 @@ class GZipMiddlewareTest(SimpleTestCase):
|
|||
with gzip.GzipFile(mode='rb', fileobj=BytesIO(gzipped_string)) as f:
|
||||
return f.read()
|
||||
|
||||
@staticmethod
|
||||
def get_mtime(gzipped_string):
|
||||
with gzip.GzipFile(mode='rb', fileobj=BytesIO(gzipped_string)) as f:
|
||||
f.read() # must read the data before accessing the header
|
||||
return f.mtime
|
||||
|
||||
def test_compress_response(self):
|
||||
"""
|
||||
Compression is performed on responses with compressible content.
|
||||
|
@ -850,6 +856,20 @@ class GZipMiddlewareTest(SimpleTestCase):
|
|||
self.assertEqual(r.content, self.incompressible_string)
|
||||
self.assertIsNone(r.get('Content-Encoding'))
|
||||
|
||||
def test_compress_deterministic(self):
|
||||
"""
|
||||
Compression results are the same for the same content and don't
|
||||
include a modification time (since that would make the results
|
||||
of compression non-deterministic and prevent
|
||||
ConditionalGetMiddleware from recognizing conditional matches
|
||||
on gzipped content).
|
||||
"""
|
||||
r1 = GZipMiddleware().process_response(self.req, self.resp)
|
||||
r2 = GZipMiddleware().process_response(self.req, self.resp)
|
||||
self.assertEqual(r1.content, r2.content)
|
||||
self.assertEqual(self.get_mtime(r1.content), 0)
|
||||
self.assertEqual(self.get_mtime(r2.content), 0)
|
||||
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango21Warning)
|
||||
@override_settings(USE_ETAGS=True)
|
||||
|
|
Loading…
Reference in New Issue