summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst4
-rw-r--r--cherrypy/lib/encoding.py24
-rw-r--r--cherrypy/test/test_tools.py18
3 files changed, 44 insertions, 2 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index c72ab792..898f1f74 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,6 +1,10 @@
v17.4.3
-------
+* :issue:`1849` via :pr:`1879`: Fixed XLF flag in gzip header
+ emitted by gzip compression tool per
+ :rfc:`1952#section-2.3.1`.
+
* :issue:`1874`: Restricted depending on pywin32 only under
CPython so that it won't get pulled-in under PyPy
-- by :user:`webknjaz`.
diff --git a/cherrypy/lib/encoding.py b/cherrypy/lib/encoding.py
index 3d001ca6..4f9495e4 100644
--- a/cherrypy/lib/encoding.py
+++ b/cherrypy/lib/encoding.py
@@ -11,6 +11,10 @@ from cherrypy.lib import is_closable_iterator
from cherrypy.lib import set_vary_header
+_COMPRESSION_LEVEL_FAST = 1
+_COMPRESSION_LEVEL_BEST = 9
+
+
def decode(encoding=None, default_encoding='utf-8'):
"""Replace or extend the list of charsets used to decode a request entity.
@@ -287,13 +291,29 @@ def compress(body, compress_level):
"""Compress 'body' at the given compress_level."""
import zlib
- # See http://www.gzip.org/zlib/rfc-gzip.html
+ # See https://tools.ietf.org/html/rfc1952
yield b'\x1f\x8b' # ID1 and ID2: gzip marker
yield b'\x08' # CM: compression method
yield b'\x00' # FLG: none set
# MTIME: 4 bytes
yield struct.pack('<L', int(time.time()) & int('FFFFFFFF', 16))
- yield b'\x02' # XFL: max compression, slowest algo
+
+ # RFC 1952, section 2.3.1:
+ #
+ # XFL (eXtra FLags)
+ # These flags are available for use by specific compression
+ # methods. The "deflate" method (CM = 8) sets these flags as
+ # follows:
+ #
+ # XFL = 2 - compressor used maximum compression,
+ # slowest algorithm
+ # XFL = 4 - compressor used fastest algorithm
+ if compress_level == _COMPRESSION_LEVEL_BEST:
+ yield b'\x02' # XFL: max compression, slowest algo
+ elif compress_level == _COMPRESSION_LEVEL_FAST:
+ yield b'\x04' # XFL: min compression, fastest algo
+ else:
+ yield b'\x00' # XFL: compression unset/tradeoff
yield b'\xff' # OS: unknown
crc = zlib.crc32(b'')
diff --git a/cherrypy/test/test_tools.py b/cherrypy/test/test_tools.py
index a73a3898..dbed9faf 100644
--- a/cherrypy/test/test_tools.py
+++ b/cherrypy/test/test_tools.py
@@ -18,6 +18,17 @@ from cherrypy._cpcompat import ntou
from cherrypy.test import helper, _test_decorators
+PY_VER_PATCH = sys.version_info[:3]
+PY_VER_MINOR = PY_VER_PATCH[:2]
+# Refs:
+# bugs.python.org/issue39389
+# docs.python.org/3.7/whatsnew/changelog.html#python-3-7-7-release-candidate-1
+# docs.python.org/3.8/whatsnew/changelog.html#python-3-8-2-release-candidate-1
+HAS_GZIP_COMPRESSION_HEADER_FIXED = PY_VER_PATCH >= (3, 8, 2) or (
+ PY_VER_MINOR == (3, 7) and PY_VER_PATCH >= (3, 7, 7)
+)
+
+
timeout = 0.2
europoundUnicode = ntou('\x80\xa3')
@@ -363,6 +374,13 @@ class ToolTests(helper.CPWebCase):
('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7')])
self.assertInBody(zbuf.getvalue()[:3])
+ if not HAS_GZIP_COMPRESSION_HEADER_FIXED:
+ # NOTE: CherryPy adopts a fix from the CPython bug 39389
+ # NOTE: introducing a variable compression XFL flag that
+ # NOTE: was hardcoded to "best compression" before. And so
+ # NOTE: we can only test it on CPython versions that also
+ # NOTE: implement this fix.
+ return
zbuf = io.BytesIO()
zfile = gzip.GzipFile(mode='wb', fileobj=zbuf, compresslevel=6)
zfile.write(expectedResult)