Skip to content
Merged
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
2 changes: 2 additions & 0 deletions docs/project/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Improvements
* :func:`~asyncio.client.connect` now closes connections with close code 1011
(internal error) when exiting the context manager with an exception.

* Improved error handling when a compression window of size 8 is requested.

Bug fixes
.........

Expand Down
30 changes: 22 additions & 8 deletions src/websockets/extensions/permessage_deflate.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ def __init__(
assert remote_no_context_takeover in [False, True]
assert local_no_context_takeover in [False, True]
assert 8 <= remote_max_window_bits <= 15
assert 8 <= local_max_window_bits <= 15
# Due to https://github.com/madler/zlib/issues/171, zlib.compressobj
# rejects wbits=-8 with ValueError: Invalid initialization option.
# This makes it impossible to support local_max_window_bits = 8.
assert 9 <= local_max_window_bits <= 15
assert "wbits" not in compress_settings

self.remote_no_context_takeover = remote_no_context_takeover
Expand Down Expand Up @@ -314,11 +317,13 @@ class ClientPerMessageDeflateFactory(ClientExtensionFactory):
server_max_window_bits: Maximum size of the server's LZ77 sliding window
in bits, between 8 and 15.
client_max_window_bits: Maximum size of the client's LZ77 sliding window
in bits, between 8 and 15, or :obj:`True` to indicate support without
setting a limit.
in bits, between 9 and 15, or :obj:`True` to indicate support without
setting a limit. 8 isn't supported due to a `limitation of zlib`_.
compress_settings: Additional keyword arguments for :func:`zlib.compressobj`,
excluding ``wbits``.

.. _limitation of zlib: https://github.com/madler/zlib/issues/171

"""

name = ExtensionName("permessage-deflate")
Expand All @@ -340,9 +345,9 @@ def __init__(
if not (
client_max_window_bits is None
or client_max_window_bits is True
or 8 <= client_max_window_bits <= 15
or 9 <= client_max_window_bits <= 15
):
raise ValueError("client_max_window_bits must be between 8 and 15")
raise ValueError("client_max_window_bits must be between 9 and 15")
if compress_settings is not None and "wbits" in compress_settings:
raise ValueError(
"compress_settings must not include wbits, "
Expand Down Expand Up @@ -464,6 +469,9 @@ def process_response_params(
elif client_max_window_bits > self.client_max_window_bits:
raise NegotiationError("unsupported client_max_window_bits")

if client_max_window_bits == 8:
raise NegotiationError("unsupported client_max_window_bits = 8, use 9")

return PerMessageDeflate(
server_no_context_takeover, # remote_no_context_takeover
client_no_context_takeover, # local_no_context_takeover
Expand Down Expand Up @@ -512,7 +520,8 @@ class ServerPerMessageDeflateFactory(ServerExtensionFactory):
server_no_context_takeover: Prevent server from using context takeover.
client_no_context_takeover: Prevent client from using context takeover.
server_max_window_bits: Maximum size of the server's LZ77 sliding window
in bits, between 8 and 15.
in bits, between 9 and 15. 8 isn't supported due to a `limitation of
zlib`_.
client_max_window_bits: Maximum size of the client's LZ77 sliding window
in bits, between 8 and 15.
compress_settings: Additional keyword arguments for :func:`zlib.compressobj`,
Expand All @@ -522,6 +531,8 @@ class ServerPerMessageDeflateFactory(ServerExtensionFactory):
the default behavior is to enable compression without enforcing
``client_max_window_bits``.

.. _limitation of zlib: https://github.com/madler/zlib/issues/171

"""

name = ExtensionName("permessage-deflate")
Expand All @@ -539,8 +550,8 @@ def __init__(
Configure the Per-Message Deflate extension factory.

"""
if not (server_max_window_bits is None or 8 <= server_max_window_bits <= 15):
raise ValueError("server_max_window_bits must be between 8 and 15")
if not (server_max_window_bits is None or 9 <= server_max_window_bits <= 15):
raise ValueError("server_max_window_bits must be between 9 and 15")
if not (client_max_window_bits is None or 8 <= client_max_window_bits <= 15):
raise ValueError("client_max_window_bits must be between 8 and 15")
if compress_settings is not None and "wbits" in compress_settings:
Expand Down Expand Up @@ -633,6 +644,9 @@ def process_request_params(
elif server_max_window_bits > self.server_max_window_bits:
server_max_window_bits = self.server_max_window_bits

if server_max_window_bits == 8:
raise NegotiationError("unsupported server_max_window_bits = 8, use 9")

# client_max_window_bits

# Config Req. Resp.
Expand Down
39 changes: 37 additions & 2 deletions tests/extensions/test_permessage_deflate.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def test_init(self):
for config in [
(False, False, 8, None), # server_max_window_bits ≥ 8
(False, True, 15, None), # server_max_window_bits ≤ 15
(True, False, None, 8), # client_max_window_bits ≥ 8
(True, False, None, 9), # client_max_window_bits ≥ 9
(True, True, None, 15), # client_max_window_bits ≤ 15
(False, False, None, True), # client_max_window_bits
(False, False, None, None, {"memLevel": 4}),
Expand All @@ -337,6 +337,7 @@ def test_init_error(self):
for config in [
(False, False, 7, 8), # server_max_window_bits < 8
(False, True, 8, 7), # client_max_window_bits < 8
(False, True, 8, 8), # client_max_window_bits = 8
(True, False, 16, 15), # server_max_window_bits > 15
(True, True, 15, 16), # client_max_window_bits > 15
(False, False, True, None), # server_max_window_bits
Expand Down Expand Up @@ -463,11 +464,21 @@ def test_process_response_params(self):
[("server_max_window_bits", "7")],
NegotiationError,
),
(
(False, False, None, None),
[("server_max_window_bits", "8")],
(False, False, 8, 15),
),
(
(False, False, None, None),
[("server_max_window_bits", "10")],
(False, False, 10, 15),
),
(
(False, False, None, None),
[("server_max_window_bits", "15")],
(False, False, 15, 15),
),
(
(False, False, None, None),
[("server_max_window_bits", "16")],
Expand Down Expand Up @@ -519,6 +530,11 @@ def test_process_response_params(self):
[("client_max_window_bits", "7")],
NegotiationError,
),
(
(False, False, None, True),
[("client_max_window_bits", "8")],
NegotiationError,
),
Comment thread
aaugustin marked this conversation as resolved.
(
(False, False, None, True),
[("client_max_window_bits", "10")],
Expand Down Expand Up @@ -662,7 +678,7 @@ def test_name(self):

def test_init(self):
for config in [
(False, False, 8, None), # server_max_window_bits ≥ 8
(False, False, 9, None), # server_max_window_bits ≥ 9
(False, True, 15, None), # server_max_window_bits ≤ 15
(True, False, None, 8), # client_max_window_bits ≥ 8
(True, True, None, 15), # client_max_window_bits ≤ 15
Expand All @@ -677,6 +693,7 @@ def test_init_error(self):
for config in [
(False, False, 7, 8), # server_max_window_bits < 8
(False, True, 8, 7), # client_max_window_bits < 8
(False, False, 8, 8), # server_max_window_bits = 8
(True, False, 16, 15), # server_max_window_bits > 15
(True, True, 15, 16), # client_max_window_bits > 15
(False, False, None, True), # client_max_window_bits
Expand Down Expand Up @@ -774,6 +791,12 @@ def test_process_request_params(self):
None,
NegotiationError,
),
(
(False, False, None, None),
[("server_max_window_bits", "8")],
None,
NegotiationError,
),
Comment thread
aaugustin marked this conversation as resolved.
(
(False, False, None, None),
[("server_max_window_bits", "10")],
Expand Down Expand Up @@ -835,12 +858,24 @@ def test_process_request_params(self):
None,
InvalidParameterValue,
),
(
(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")],
[("client_max_window_bits", "10")], # doesn't matter
(False, False, 10, 15),
),
(
(False, False, None, None),
[("client_max_window_bits", "15")],
[("client_max_window_bits", "15")], # doesn't matter
(False, False, 15, 15),
),
(
(False, False, None, None),
[("client_max_window_bits", "16")],
Expand Down
Loading