Skip to content

Commit

Permalink
Fix deflate compression (#4506)
Browse files Browse the repository at this point in the history
  • Loading branch information
socketpair committed Jan 17, 2020
1 parent 6d2b136 commit 0e0f6af
Show file tree
Hide file tree
Showing 11 changed files with 17 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGES/4506.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed 'deflate' compressions. According to RFC 2616 now.
2 changes: 1 addition & 1 deletion aiohttp/http_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -727,7 +727,7 @@ def flush(self) -> bytes:
self.decompressor = BrotliDecoder() # type: Any
else:
zlib_mode = (16 + zlib.MAX_WBITS
if encoding == 'gzip' else -zlib.MAX_WBITS)
if encoding == 'gzip' else zlib.MAX_WBITS)
self.decompressor = zlib.decompressobj(wbits=zlib_mode)

def set_exception(self, exc: BaseException) -> None:
Expand Down
8 changes: 5 additions & 3 deletions aiohttp/http_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def feed_data(self, data: bytes) -> Tuple[bool, bytes]:
def _feed_data(self, data: bytes) -> Tuple[bool, bytes]:
for fin, opcode, payload, compressed in self.parse_frame(data):
if compressed and not self._decompressobj:
self._decompressobj = zlib.decompressobj(wbits=-zlib.MAX_WBITS)
self._decompressobj = zlib.decompressobj(wbits=zlib.MAX_WBITS)
if opcode == WSMsgType.CLOSE:
if len(payload) >= 2:
close_code = UNPACK_CLOSE_CODE(payload[:2])[0]
Expand Down Expand Up @@ -567,11 +567,13 @@ async def _send_frame(self, message: bytes, opcode: int,
# if self.compress and opcode < 8 and len(message) > 124:
if (compress or self.compress) and opcode < 8:
if compress:
assert compress > 0
# Do not set self._compress if compressing is for this frame
compressobj = zlib.compressobj(wbits=-compress)
compressobj = zlib.compressobj(wbits=compress)
else: # self.compress
if not self._compressobj:
self._compressobj = zlib.compressobj(wbits=-self.compress)
assert self.compress > 0
self._compressobj = zlib.compressobj(wbits=self.compress)
compressobj = self._compressobj

message = compressobj.compress(message)
Expand Down
2 changes: 1 addition & 1 deletion aiohttp/http_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def enable_chunking(self) -> None:

def enable_compression(self, encoding: str='deflate') -> None:
zlib_mode = (16 + zlib.MAX_WBITS
if encoding == 'gzip' else -zlib.MAX_WBITS)
if encoding == 'gzip' else zlib.MAX_WBITS)
self._compress = zlib.compressobj(wbits=zlib_mode)

def _write(self, chunk: bytes) -> None:
Expand Down
4 changes: 2 additions & 2 deletions aiohttp/multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def _decode_content(self, data: bytes) -> bytes:
encoding = self.headers.get(CONTENT_ENCODING, '').lower()

if encoding == 'deflate':
return zlib.decompress(data, -zlib.MAX_WBITS)
return zlib.decompress(data, zlib.MAX_WBITS)
elif encoding == 'gzip':
return zlib.decompress(data, 16 + zlib.MAX_WBITS)
elif encoding == 'identity':
Expand Down Expand Up @@ -978,7 +978,7 @@ def enable_encoding(self, encoding: str) -> None:

def enable_compression(self, encoding: str='deflate') -> None:
zlib_mode = (16 + zlib.MAX_WBITS
if encoding == 'gzip' else -zlib.MAX_WBITS)
if encoding == 'gzip' else zlib.MAX_WBITS)
self._compress = zlib.compressobj(wbits=zlib_mode)

async def write_eof(self) -> None:
Expand Down
3 changes: 2 additions & 1 deletion aiohttp/web_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ async def _start(self, request: 'BaseRequest') -> AbstractStreamWriter:
return await super()._start(request)

def _compress_body(self, zlib_mode: int) -> None:
assert zlib_mode > 0
compressobj = zlib.compressobj(wbits=zlib_mode)
body_in = self._body
assert body_in is not None
Expand All @@ -683,7 +684,7 @@ async def _do_start_compression(self, coding: ContentCoding) -> None:
# Instead of using _payload_writer.enable_compression,
# compress the whole body
zlib_mode = (16 + zlib.MAX_WBITS
if coding == ContentCoding.gzip else -zlib.MAX_WBITS)
if coding == ContentCoding.gzip else zlib.MAX_WBITS)
body_in = self._body
assert body_in is not None
if self._zlib_executor_size is not None and \
Expand Down
2 changes: 1 addition & 1 deletion tests/test_http_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,7 @@ async def test_http_payload_parser_length(self, stream) -> None:
assert b'12' == b''.join(d for d, _ in out._buffer)
assert b'45' == tail

_comp = zlib.compressobj(wbits=-zlib.MAX_WBITS)
_comp = zlib.compressobj(wbits=zlib.MAX_WBITS)
_COMPRESSED = b''.join([_comp.compress(b'data'), _comp.flush()])

async def test_http_payload_parser_deflate(self, stream) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_http_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ async def test_write_payload_chunked_filter_mutiple_chunks(
b'2\r\na2\r\n0\r\n\r\n')


compressor = zlib.compressobj(wbits=-zlib.MAX_WBITS)
compressor = zlib.compressobj(wbits=zlib.MAX_WBITS)
COMPRESSED = b''.join([compressor.compress(b'data'), compressor.flush()])


Expand Down
2 changes: 1 addition & 1 deletion tests/test_web_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ async def test_response_with_precompressed_body_deflate(

async def handler(request):
headers = {'Content-Encoding': 'deflate'}
zcomp = zlib.compressobj(wbits=-zlib.MAX_WBITS)
zcomp = zlib.compressobj(wbits=zlib.MAX_WBITS)
data = zcomp.compress(b'mydata') + zcomp.flush()
return web.Response(body=data, headers=headers)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_web_sendfile_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ async def handler(request):

resp = await client.get('/')
assert resp.status == 200
zcomp = zlib.compressobj(wbits=-zlib.MAX_WBITS)
zcomp = zlib.compressobj(wbits=zlib.MAX_WBITS)
expected_body = zcomp.compress(b'file content\n') + zcomp.flush()
assert expected_body == await resp.read()
assert 'application/octet-stream' == resp.headers['Content-Type']
Expand Down
2 changes: 1 addition & 1 deletion tests/test_websocket_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def build_frame(message, opcode, use_mask=False, noheader=False, is_fin=True,
compress=False):
# Send a frame over the websocket with message as its payload.
if compress:
compressobj = zlib.compressobj(wbits=-9)
compressobj = zlib.compressobj(wbits=9)
message = compressobj.compress(message)
message = message + compressobj.flush(zlib.Z_SYNC_FLUSH)
if message.endswith(_WS_DEFLATE_TRAILING):
Expand Down

0 comments on commit 0e0f6af

Please sign in to comment.