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.
|
# Used with permission.
|
||||||
def compress_string(s):
|
def compress_string(s):
|
||||||
zbuf = BytesIO()
|
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)
|
zfile.write(s)
|
||||||
return zbuf.getvalue()
|
return zbuf.getvalue()
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ class StreamingBuffer(object):
|
||||||
# Like compress_string, but for iterators of strings.
|
# Like compress_string, but for iterators of strings.
|
||||||
def compress_sequence(sequence):
|
def compress_sequence(sequence):
|
||||||
buf = StreamingBuffer()
|
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...
|
# Output headers...
|
||||||
yield buf.read()
|
yield buf.read()
|
||||||
for item in sequence:
|
for item in sequence:
|
||||||
|
|
|
@ -770,6 +770,12 @@ class GZipMiddlewareTest(SimpleTestCase):
|
||||||
with gzip.GzipFile(mode='rb', fileobj=BytesIO(gzipped_string)) as f:
|
with gzip.GzipFile(mode='rb', fileobj=BytesIO(gzipped_string)) as f:
|
||||||
return f.read()
|
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):
|
def test_compress_response(self):
|
||||||
"""
|
"""
|
||||||
Compression is performed on responses with compressible content.
|
Compression is performed on responses with compressible content.
|
||||||
|
@ -850,6 +856,20 @@ class GZipMiddlewareTest(SimpleTestCase):
|
||||||
self.assertEqual(r.content, self.incompressible_string)
|
self.assertEqual(r.content, self.incompressible_string)
|
||||||
self.assertIsNone(r.get('Content-Encoding'))
|
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)
|
@ignore_warnings(category=RemovedInDjango21Warning)
|
||||||
@override_settings(USE_ETAGS=True)
|
@override_settings(USE_ETAGS=True)
|
||||||
|
|
Loading…
Reference in New Issue