diff options
-rw-r--r-- | CHANGES.rst | 4 | ||||
-rw-r--r-- | cherrypy/lib/encoding.py | 24 | ||||
-rw-r--r-- | cherrypy/test/test_tools.py | 18 |
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) |