Skip to content

Commit cf8a2c5

Browse files
committed
Add ENABLE_CONNECT_PROTOCOL setting support
This setting flag is introduced in RFC 8441 to allow websockets over HTTP/2. It has only recently been added to Hyperframe (hence the version bump).
1 parent 0c5b6ad commit cf8a2c5

File tree

4 files changed

+60
-6
lines changed

4 files changed

+60
-6
lines changed

HISTORY.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ API Changes (Backward-Compatible)
2020
fired when a PING frame with an ACK flag is received.
2121
``h2.events.PingAcknowledged`` is deprecated in favour of the identical
2222
``h2.events.PingAckReceived``.
23+
- Added ``ENABLE_CONNECT_PROTOCOL`` to ``h2.settings.SettingCodes``.
2324

2425
Bugfixes
2526
~~~~~~~~

h2/settings.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ class SettingCodes(enum.IntEnum):
5555
#: and value in octets plus an overhead of 32 octets for each header field.
5656
MAX_HEADER_LIST_SIZE = SettingsFrame.MAX_HEADER_LIST_SIZE
5757

58+
#: This setting can be used to enable the connect protocol. To enable on a
59+
#: client set this to 1.
60+
ENABLE_CONNECT_PROTOCOL = SettingsFrame.ENABLE_CONNECT_PROTOCOL
61+
5862

5963
def _setting_code_from_int(code):
6064
"""
@@ -141,6 +145,7 @@ def __init__(self, client=True, initial_values=None):
141145
SettingCodes.ENABLE_PUSH: collections.deque([int(client)]),
142146
SettingCodes.INITIAL_WINDOW_SIZE: collections.deque([65535]),
143147
SettingCodes.MAX_FRAME_SIZE: collections.deque([16384]),
148+
SettingCodes.ENABLE_CONNECT_PROTOCOL: collections.deque([0]),
144149
}
145150
if initial_values is not None:
146151
for key, value in initial_values.items():
@@ -249,6 +254,18 @@ def max_header_list_size(self):
249254
def max_header_list_size(self, value):
250255
self[SettingCodes.MAX_HEADER_LIST_SIZE] = value
251256

257+
@property
258+
def enable_connect_protocol(self):
259+
"""
260+
The current value of the :data:`ENABLE_CONNECT_PROTOCOL
261+
<h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL>` setting.
262+
"""
263+
return self[SettingCodes.ENABLE_CONNECT_PROTOCOL]
264+
265+
@enable_connect_protocol.setter
266+
def enable_connect_protocol(self, value):
267+
self[SettingCodes.ENABLE_CONNECT_PROTOCOL] = value
268+
252269
# Implement the MutableMapping API.
253270
def __getitem__(self, key):
254271
val = self._settings[key][0]
@@ -298,7 +315,7 @@ def __ne__(self, other):
298315
return NotImplemented
299316

300317

301-
def _validate_setting(setting, value):
318+
def _validate_setting(setting, value): # noqa: C901
302319
"""
303320
Confirms that a specific setting has a well-formed value. If the setting is
304321
invalid, returns an error code. Otherwise, returns 0 (NO_ERROR).
@@ -315,5 +332,8 @@ def _validate_setting(setting, value):
315332
elif setting == SettingCodes.MAX_HEADER_LIST_SIZE:
316333
if value < 0:
317334
return ErrorCodes.PROTOCOL_ERROR
335+
elif setting == SettingCodes.ENABLE_CONNECT_PROTOCOL:
336+
if value not in (0, 1):
337+
return ErrorCodes.PROTOCOL_ERROR
318338

319339
return 0

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
'Programming Language :: Python :: Implementation :: PyPy',
6363
],
6464
install_requires=[
65-
'hyperframe>=5.1.0, <6',
65+
'hyperframe>=5.2.0, <6',
6666
'hpack>=2.3,<4',
6767
],
6868
extras_require={

test/test_settings.py

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def test_settings_defaults_client(self):
3131
assert s[h2.settings.SettingCodes.ENABLE_PUSH] == 1
3232
assert s[h2.settings.SettingCodes.INITIAL_WINDOW_SIZE] == 65535
3333
assert s[h2.settings.SettingCodes.MAX_FRAME_SIZE] == 16384
34+
assert s[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] == 0
3435

3536
def test_settings_defaults_server(self):
3637
"""
@@ -42,6 +43,7 @@ def test_settings_defaults_server(self):
4243
assert s[h2.settings.SettingCodes.ENABLE_PUSH] == 0
4344
assert s[h2.settings.SettingCodes.INITIAL_WINDOW_SIZE] == 65535
4445
assert s[h2.settings.SettingCodes.MAX_FRAME_SIZE] == 16384
46+
assert s[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] == 0
4547

4648
@pytest.mark.parametrize('client', [True, False])
4749
def test_can_set_initial_values(self, client):
@@ -54,6 +56,7 @@ def test_can_set_initial_values(self, client):
5456
h2.settings.SettingCodes.MAX_FRAME_SIZE: 16388,
5557
h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 100,
5658
h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE: 2**16,
59+
h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL: 1,
5760
}
5861
s = h2.settings.Settings(client=client, initial_values=overrides)
5962

@@ -63,6 +66,7 @@ def test_can_set_initial_values(self, client):
6366
assert s[h2.settings.SettingCodes.MAX_FRAME_SIZE] == 16388
6467
assert s[h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS] == 100
6568
assert s[h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE] == 2**16
69+
assert s[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] == 1
6670

6771
@pytest.mark.parametrize(
6872
'setting,value',
@@ -74,6 +78,7 @@ def test_can_set_initial_values(self, client):
7478
(h2.settings.SettingCodes.MAX_FRAME_SIZE, 1),
7579
(h2.settings.SettingCodes.MAX_FRAME_SIZE, 2**30),
7680
(h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE, -1),
81+
(h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL, -1),
7782
]
7883
)
7984
def test_cannot_set_invalid_initial_values(self, setting, value):
@@ -108,6 +113,7 @@ def test_acknowledging_values(self):
108113
h2.settings.SettingCodes.ENABLE_PUSH: 0,
109114
h2.settings.SettingCodes.INITIAL_WINDOW_SIZE: 60,
110115
h2.settings.SettingCodes.MAX_FRAME_SIZE: 16385,
116+
h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL: 1,
111117
}
112118
s.update(new_settings)
113119

@@ -171,16 +177,16 @@ def test_length_correctly_reported(self):
171177
Length is related only to the number of keys.
172178
"""
173179
s = h2.settings.Settings(client=True)
174-
assert len(s) == 4
180+
assert len(s) == 5
175181

176182
s[h2.settings.SettingCodes.HEADER_TABLE_SIZE] == 8000
177-
assert len(s) == 4
183+
assert len(s) == 5
178184

179185
s.acknowledge()
180-
assert len(s) == 4
186+
assert len(s) == 5
181187

182188
del s[h2.settings.SettingCodes.HEADER_TABLE_SIZE]
183-
assert len(s) == 3
189+
assert len(s) == 4
184190

185191
def test_new_values_work(self):
186192
"""
@@ -234,6 +240,9 @@ def test_settings_getters(self):
234240
assert s.max_frame_size == s[h2.settings.SettingCodes.MAX_FRAME_SIZE]
235241
assert s.max_concurrent_streams == 2**32 + 1 # A sensible default.
236242
assert s.max_header_list_size is None
243+
assert s.enable_connect_protocol == s[
244+
h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL
245+
]
237246

238247
def test_settings_setters(self):
239248
"""
@@ -247,6 +256,7 @@ def test_settings_setters(self):
247256
s.max_frame_size = 16385
248257
s.max_concurrent_streams = 4
249258
s.max_header_list_size = 2**16
259+
s.enable_connect_protocol = 1
250260

251261
s.acknowledge()
252262
assert s[h2.settings.SettingCodes.HEADER_TABLE_SIZE] == 0
@@ -255,6 +265,7 @@ def test_settings_setters(self):
255265
assert s[h2.settings.SettingCodes.MAX_FRAME_SIZE] == 16385
256266
assert s[h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS] == 4
257267
assert s[h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE] == 2**16
268+
assert s[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] == 1
258269

259270
@given(integers())
260271
def test_cannot_set_invalid_values_for_enable_push(self, val):
@@ -363,6 +374,28 @@ def test_cannot_set_invalid_values_for_max_header_list_size(self, val):
363374
with pytest.raises(KeyError):
364375
s[h2.settings.SettingCodes.MAX_HEADER_LIST_SIZE]
365376

377+
@given(integers())
378+
def test_cannot_set_invalid_values_for_enable_connect_protocol(self, val):
379+
"""
380+
SETTINGS_ENABLE_CONNECT_PROTOCOL only allows two values: 0, 1.
381+
"""
382+
assume(val not in (0, 1))
383+
s = h2.settings.Settings()
384+
385+
with pytest.raises(h2.exceptions.InvalidSettingsValueError) as e:
386+
s.enable_connect_protocol = val
387+
388+
s.acknowledge()
389+
assert e.value.error_code == h2.errors.ErrorCodes.PROTOCOL_ERROR
390+
assert s.enable_connect_protocol == 0
391+
392+
with pytest.raises(h2.exceptions.InvalidSettingsValueError) as e:
393+
s[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] = val
394+
395+
s.acknowledge()
396+
assert e.value.error_code == h2.errors.ErrorCodes.PROTOCOL_ERROR
397+
assert s[h2.settings.SettingCodes.ENABLE_CONNECT_PROTOCOL] == 0
398+
366399

367400
class TestSettingsEquality(object):
368401
"""

0 commit comments

Comments
 (0)