Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/project/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ Improvements
* :func:`~asyncio.client.connect` now closes connections with close code 1011
(internal error) when exiting the context manager with an exception.

Bug fixes
.........

* Negotiating ``permessage-deflate`` no longer fails with a :exc:`ValueError`
when the peer asks for a window of 8 bits on the side that compresses.
:mod:`zlib` cannot create a raw deflate compressor with such a window, so
the extension is declined instead.

.. _17.1:

17.1
Expand Down
13 changes: 13 additions & 0 deletions src/websockets/extensions/permessage_deflate.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,12 @@ def process_response_params(
elif client_max_window_bits > self.client_max_window_bits:
raise NegotiationError("unsupported client_max_window_bits")

# zlib cannot build a raw deflate compressor with an 8 bit window, so
# 8 is unusable for the window we compress with, even though it is a
# legal value on the wire and fine for the window we decompress with.
if client_max_window_bits == 8:
raise NegotiationError("unsupported client_max_window_bits")

return PerMessageDeflate(
server_no_context_takeover, # remote_no_context_takeover
client_no_context_takeover, # local_no_context_takeover
Expand Down Expand Up @@ -658,6 +664,13 @@ def process_request_params(
elif self.client_max_window_bits < client_max_window_bits:
client_max_window_bits = self.client_max_window_bits

# zlib cannot build a raw deflate compressor with an 8 bit window, so
# 8 is unusable for the window we compress with, even though it is a
# legal value on the wire and fine for the window we decompress with.
# RFC 7692 7.1.2.1 lets a server decline an offer it cannot support.
if server_max_window_bits == 8:
raise NegotiationError("unsupported server_max_window_bits")

return (
_build_parameters(
server_no_context_takeover,
Expand Down
27 changes: 27 additions & 0 deletions tests/extensions/test_permessage_deflate.py
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,12 @@ def test_process_response_params(self):
[("server_max_window_bits", "7")],
NegotiationError,
),
(
# 8 is legal for the window we decompress with.
(False, False, None, None),
[("server_max_window_bits", "8")],
(False, False, 8, 15),
),
(
(False, False, None, None),
[("server_max_window_bits", "10")],
Expand Down Expand Up @@ -519,6 +525,12 @@ def test_process_response_params(self):
[("client_max_window_bits", "7")],
NegotiationError,
),
(
# 8 is unusable for the window we compress with.
(False, False, None, True),
[("client_max_window_bits", "8")],
NegotiationError,
),
(
(False, False, None, True),
[("client_max_window_bits", "10")],
Expand Down Expand Up @@ -774,6 +786,14 @@ def test_process_request_params(self):
None,
NegotiationError,
),
(
# 8 is unusable for the window we compress with; RFC 7692
# 7.1.2.1 lets the server decline the offer.
(False, False, None, None),
[("server_max_window_bits", "8")],
None,
NegotiationError,
),
(
(False, False, None, None),
[("server_max_window_bits", "10")],
Expand Down Expand Up @@ -835,6 +855,13 @@ def test_process_request_params(self):
None,
InvalidParameterValue,
),
(
# 8 is legal for the window we decompress with.
(False, False, None, None),
[("client_max_window_bits", "8")],
[("client_max_window_bits", "8")], # doesn't matter
(False, False, 8, 15),
),
(
(False, False, None, None),
[("client_max_window_bits", "10")],
Expand Down