Skip to content
This repository was archived by the owner on Jan 13, 2021. It is now read-only.

Commit 7a4461e

Browse files
committed
Frame header is 9 bytes, not 8.
1 parent b8f0b3d commit 7a4461e

File tree

4 files changed

+71
-83
lines changed

4 files changed

+71
-83
lines changed

hyper/http20/connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,8 +478,8 @@ def _recv_cb(self):
478478
read a frame that doesn't belong to them. That's ok: streams need to
479479
make a decision to spin around again.
480480
"""
481-
# Begin by reading 8 bytes from the socket.
482-
header = self._sock.recv(8)
481+
# Begin by reading 9 bytes from the socket.
482+
header = self._sock.recv(9)
483483

484484
# Parse the header. We can use the returned memoryview directly here.
485485
frame, length = Frame.parse_frame_header(header)

hyper/http20/frame.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@ def __init__(self, stream_id):
3939
@staticmethod
4040
def parse_frame_header(header):
4141
"""
42-
Takes an 8-byte frame header and returns a tuple of the appropriate
42+
Takes an 9-byte frame header and returns a tuple of the appropriate
4343
Frame object and the length that needs to be read from the socket.
4444
"""
45-
fields = struct.unpack("!HBBL", header)
46-
length = fields[0] & 0x3FFF
47-
type = fields[1]
48-
flags = fields[2]
49-
stream_id = fields[3]
45+
fields = struct.unpack("!HBBBL", header)
46+
# First 24 bits are frame length.
47+
length = (fields[0] << 8) + fields[1]
48+
type = fields[2]
49+
flags = fields[3]
50+
stream_id = fields[4]
5051

5152
frame = FRAMES[type](stream_id)
5253
frame.parse_flags(flags)
@@ -61,6 +62,7 @@ def parse_flags(self, flag_byte):
6162

6263
def serialize(self):
6364
body = self.serialize_body()
65+
body_len = len(body)
6466

6567
# Build the common frame header.
6668
# First, get the flags.
@@ -71,8 +73,9 @@ def serialize(self):
7173
flags |= flag_bit
7274

7375
header = struct.pack(
74-
"!HBBL",
75-
len(body) & 0x3FFF, # Length must have the top two bits unset.
76+
"!HBBBL",
77+
body_len & 0xFFFF00, # Length is spread over top 24 bits
78+
body_len & 0x0000FF,
7679
self.type,
7780
flags,
7881
self.stream_id & 0x7FFFFFFF # Stream ID is 32 bits.

test/test_hyper.py

Lines changed: 55 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626

2727

2828
def decode_frame(frame_data):
29-
f, length = Frame.parse_frame_header(frame_data[:8])
30-
f.parse_body(frame_data[8:8 + length])
31-
assert 8 + length == len(frame_data)
29+
f, length = Frame.parse_frame_header(frame_data[:9])
30+
f.parse_body(memoryview(frame_data[9:9 + length]))
31+
assert 9 + length == len(frame_data)
3232
return f
3333

3434

@@ -52,8 +52,8 @@ def test_base_frame_cant_parse_body(self):
5252

5353

5454
class TestDataFrame(object):
55-
payload = b'\x00\x08\x00\x01\x00\x00\x00\x01testdata'
56-
payload_with_padding = b'\x00\x13\x00\x09\x00\x00\x00\x01\x0Atestdata' + b'\0' * 10
55+
payload = b'\x00\x00\x08\x00\x01\x00\x00\x00\x01testdata'
56+
payload_with_padding = b'\x00\x00\x13\x00\x09\x00\x00\x00\x01\x0Atestdata' + b'\0' * 10
5757

5858
def test_data_frame_has_correct_flags(self):
5959
f = DataFrame(1)
@@ -80,17 +80,15 @@ def test_data_frame_with_padding_serializes_properly(self):
8080
assert s == self.payload_with_padding
8181

8282
def test_data_frame_parses_properly(self):
83-
f, length = Frame.parse_frame_header(self.payload[:8])
84-
f.parse_body(memoryview(self.payload[8:8 + length]))
83+
f = decode_frame(self.payload)
8584

8685
assert isinstance(f, DataFrame)
8786
assert f.flags == set(['END_STREAM'])
8887
assert f.pad_length == 0
8988
assert f.data == b'testdata'
9089

9190
def test_data_frame_with_padding_parses_properly(self):
92-
f, length = Frame.parse_frame_header(self.payload_with_padding[:8])
93-
f.parse_body(memoryview(self.payload_with_padding[8:8 + length]))
91+
f = decode_frame(self.payload_with_padding)
9492

9593
assert isinstance(f, DataFrame)
9694
assert f.flags == set(['END_STREAM', 'PADDED'])
@@ -117,7 +115,7 @@ def test_data_frame_comes_on_a_stream(self):
117115

118116

119117
class TestPriorityFrame(object):
120-
payload = b'\x00\x05\x02\x00\x00\x00\x00\x01\x80\x00\x00\x04\x40'
118+
payload = b'\x00\x00\x05\x02\x00\x00\x00\x00\x01\x80\x00\x00\x04\x40'
121119

122120
def test_priority_frame_has_no_flags(self):
123121
f = PriorityFrame(1)
@@ -134,8 +132,7 @@ def test_priority_frame_with_all_data_serializes_properly(self):
134132
assert f.serialize() == self.payload
135133

136134
def test_priority_frame_with_all_data_parses_properly(self):
137-
f, length = Frame.parse_frame_header(self.payload[:8])
138-
f.parse_body(memoryview(self.payload[8:8 + length]))
135+
f = decode_frame(self.payload)
139136

140137
assert isinstance(f, PriorityFrame)
141138
assert f.flags == set()
@@ -160,12 +157,11 @@ def test_rst_stream_frame_serializes_properly(self):
160157
f.error_code = 420
161158

162159
s = f.serialize()
163-
assert s == b'\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x01\xa4'
160+
assert s == b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x01\xa4'
164161

165162
def test_rst_stream_frame_parses_properly(self):
166-
s = b'\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x01\xa4'
167-
f, length = Frame.parse_frame_header(s[:8])
168-
f.parse_body(memoryview(s[8:8 + length]))
163+
s = b'\x00\x00\x04\x03\x00\x00\x00\x00\x01\x00\x00\x01\xa4'
164+
f = decode_frame(s)
169165

170166
assert isinstance(f, RstStreamFrame)
171167
assert f.flags == set()
@@ -183,13 +179,13 @@ def test_rst_stream_frame_must_have_body_length_four(self):
183179

184180
class TestSettingsFrame(object):
185181
serialized = (
186-
b'\x00\x24\x04\x01\x00\x00\x00\x00' + # Frame header
187-
b'\x00\x01\x00\x00\x10\x00' + # HEADER_TABLE_SIZE
188-
b'\x00\x02\x00\x00\x00\x00' + # ENABLE_PUSH
189-
b'\x00\x03\x00\x00\x00\x64' + # MAX_CONCURRENT_STREAMS
190-
b'\x00\x04\x00\x00\xFF\xFF' + # INITIAL_WINDOW_SIZE
191-
b'\x00\x05\x00\x00\x40\x00' + # SETTINGS_MAX_FRAME_SIZE
192-
b'\x00\x06\x00\x00\xFF\xFF' # SETTINGS_MAX_HEADER_LIST_SIZE
182+
b'\x00\x00\x24\x04\x01\x00\x00\x00\x00' + # Frame header
183+
b'\x00\x01\x00\x00\x10\x00' + # HEADER_TABLE_SIZE
184+
b'\x00\x02\x00\x00\x00\x00' + # ENABLE_PUSH
185+
b'\x00\x03\x00\x00\x00\x64' + # MAX_CONCURRENT_STREAMS
186+
b'\x00\x04\x00\x00\xFF\xFF' + # INITIAL_WINDOW_SIZE
187+
b'\x00\x05\x00\x00\x40\x00' + # SETTINGS_MAX_FRAME_SIZE
188+
b'\x00\x06\x00\x00\xFF\xFF' # SETTINGS_MAX_HEADER_LIST_SIZE
193189
)
194190

195191
settings = {
@@ -215,8 +211,7 @@ def test_settings_frame_serializes_properly(self):
215211
assert s == self.serialized
216212

217213
def test_settings_frame_parses_properly(self):
218-
f, length = Frame.parse_frame_header(self.serialized[:8])
219-
f.parse_body(memoryview(self.serialized[8:8 + length]))
214+
f = decode_frame(self.serialized)
220215

221216
assert isinstance(f, SettingsFrame)
222217
assert f.flags == set(['ACK'])
@@ -242,19 +237,18 @@ def test_push_promise_frame_serializes_properly(self):
242237

243238
s = f.serialize()
244239
assert s == (
245-
b'\x00\x0F\x05\x04\x00\x00\x00\x01' +
240+
b'\x00\x00\x0F\x05\x04\x00\x00\x00\x01' +
246241
b'\x00\x00\x00\x04' +
247242
b'hello world'
248243
)
249244

250245
def test_push_promise_frame_parses_properly(self):
251246
s = (
252-
b'\x00\x0F\x05\x04\x00\x00\x00\x01' +
247+
b'\x00\x00\x0F\x05\x04\x00\x00\x00\x01' +
253248
b'\x00\x00\x00\x04' +
254249
b'hello world'
255250
)
256-
f, length = Frame.parse_frame_header(s[:8])
257-
f.parse_body(memoryview(s[8:8 + length]))
251+
f = decode_frame(s)
258252

259253
assert isinstance(f, PushPromiseFrame)
260254
assert f.flags == set(['END_HEADERS'])
@@ -276,7 +270,7 @@ def test_ping_frame_serializes_properly(self):
276270

277271
s = f.serialize()
278272
assert s == (
279-
b'\x00\x08\x06\x01\x00\x00\x00\x00\x01\x02\x00\x00\x00\x00\x00\x00'
273+
b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x01\x02\x00\x00\x00\x00\x00\x00'
280274
)
281275

282276
def test_no_more_than_8_octets(self):
@@ -287,9 +281,8 @@ def test_no_more_than_8_octets(self):
287281
f.serialize()
288282

289283
def test_ping_frame_parses_properly(self):
290-
s = b'\x00\x08\x06\x01\x00\x00\x00\x00\x01\x02\x00\x00\x00\x00\x00\x00'
291-
f, length = Frame.parse_frame_header(s[:8])
292-
f.parse_body(memoryview(s[8:8 + length]))
284+
s = b'\x00\x00\x08\x06\x01\x00\x00\x00\x00\x01\x02\x00\x00\x00\x00\x00\x00'
285+
f = decode_frame(s)
293286

294287
assert isinstance(f, PingFrame)
295288
assert f.flags == set(['ACK'])
@@ -321,21 +314,20 @@ def test_goaway_serializes_properly(self):
321314

322315
s = f.serialize()
323316
assert s == (
324-
b'\x00\x0D\x07\x00\x00\x00\x00\x00' + # Frame header
325-
b'\x00\x00\x00\x40' + # Last Stream ID
326-
b'\x00\x00\x00\x20' + # Error Code
327-
b'hello' # Additional data
317+
b'\x00\x00\x0D\x07\x00\x00\x00\x00\x00' + # Frame header
318+
b'\x00\x00\x00\x40' + # Last Stream ID
319+
b'\x00\x00\x00\x20' + # Error Code
320+
b'hello' # Additional data
328321
)
329322

330323
def test_goaway_frame_parses_properly(self):
331324
s = (
332-
b'\x00\x0D\x07\x00\x00\x00\x00\x00' + # Frame header
333-
b'\x00\x00\x00\x40' + # Last Stream ID
334-
b'\x00\x00\x00\x20' + # Error Code
335-
b'hello' # Additional data
325+
b'\x00\x00\x0D\x07\x00\x00\x00\x00\x00' + # Frame header
326+
b'\x00\x00\x00\x40' + # Last Stream ID
327+
b'\x00\x00\x00\x20' + # Error Code
328+
b'hello' # Additional data
336329
)
337-
f, length = Frame.parse_frame_header(s[:8])
338-
f.parse_body(memoryview(s[8:8 + length]))
330+
f = decode_frame(s)
339331

340332
assert isinstance(f, GoAwayFrame)
341333
assert f.flags == set()
@@ -359,12 +351,11 @@ def test_window_update_serializes_properly(self):
359351
f.window_increment = 512
360352

361353
s = f.serialize()
362-
assert s == b'\x00\x04\x08\x00\x00\x00\x00\x00\x00\x00\x02\x00'
354+
assert s == b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x00\x02\x00'
363355

364356
def test_windowupdate_frame_parses_properly(self):
365-
s = b'\x00\x04\x08\x00\x00\x00\x00\x00\x00\x00\x02\x00'
366-
f, length = Frame.parse_frame_header(s[:8])
367-
f.parse_body(memoryview(s[8:8 + length]))
357+
s = b'\x00\x00\x04\x08\x00\x00\x00\x00\x00\x00\x00\x02\x00'
358+
f = decode_frame(s)
368359

369360
assert isinstance(f, WindowUpdateFrame)
370361
assert f.flags == set()
@@ -386,17 +377,16 @@ def test_headers_frame_serializes_properly(self):
386377

387378
s = f.serialize()
388379
assert s == (
389-
b'\x00\x0B\x01\x05\x00\x00\x00\x01' +
380+
b'\x00\x00\x0B\x01\x05\x00\x00\x00\x01' +
390381
b'hello world'
391382
)
392383

393384
def test_headers_frame_parses_properly(self):
394385
s = (
395-
b'\x00\x0B\x01\x05\x00\x00\x00\x01' +
386+
b'\x00\x00\x0B\x01\x05\x00\x00\x00\x01' +
396387
b'hello world'
397388
)
398-
f, length = Frame.parse_frame_header(s[:8])
399-
f.parse_body(memoryview(s[8:8 + length]))
389+
f = decode_frame(s)
400390

401391
assert isinstance(f, HeadersFrame)
402392
assert f.flags == set(['END_STREAM', 'END_HEADERS'])
@@ -406,11 +396,10 @@ def test_headers_frame_with_priority_parses_properly(self):
406396
# This test also tests that we can receive a HEADERS frame with no
407397
# actual headers on it. This is technically possible.
408398
s = (
409-
b'\x00\x05\x01\x20\x00\x00\x00\x01' +
399+
b'\x00\x00\x05\x01\x20\x00\x00\x00\x01' +
410400
b'\x80\x00\x00\x04\x40'
411401
)
412-
f, length = Frame.parse_frame_header(s[:8])
413-
f.parse_body(memoryview(s[8:8 + length]))
402+
f = decode_frame(s)
414403

415404
assert isinstance(f, HeadersFrame)
416405
assert f.flags == set(['PRIORITY'])
@@ -423,7 +412,7 @@ def test_headers_frame_with_priority_serializes_properly(self):
423412
# This test also tests that we can receive a HEADERS frame with no
424413
# actual headers on it. This is technically possible.
425414
s = (
426-
b'\x00\x05\x01\x20\x00\x00\x00\x01' +
415+
b'\x00\x00\x05\x01\x20\x00\x00\x00\x01' +
427416
b'\x80\x00\x00\x04\x40'
428417
)
429418
f = HeadersFrame(1)
@@ -450,14 +439,13 @@ def test_continuation_frame_serializes(self):
450439

451440
s = f.serialize()
452441
assert s == (
453-
b'\x00\x0B\x09\x04\x00\x00\x00\x01' +
442+
b'\x00\x00\x0B\x09\x04\x00\x00\x00\x01' +
454443
b'hello world'
455444
)
456445

457446
def test_continuation_frame_parses_properly(self):
458-
s = b'\x00\x0B\x09\x04\x00\x00\x00\x01hello world'
459-
f, length = Frame.parse_frame_header(s[:8])
460-
f.parse_body(memoryview(s[8:8 + length]))
447+
s = b'\x00\x00\x0B\x09\x04\x00\x00\x00\x01hello world'
448+
f = decode_frame(s)
461449

462450
assert isinstance(f, ContinuationFrame)
463451
assert f.flags == set(['END_HEADERS'])
@@ -466,12 +454,12 @@ def test_continuation_frame_parses_properly(self):
466454

467455
class TestAltSvcFrame(object):
468456
payload_with_origin = (
469-
b'\x00\x2B\x0A\x00\x00\x00\x00\x00'
457+
b'\x00\x00\x2B\x0A\x00\x00\x00\x00\x00'
470458
b'\x00\x00\x00\x1D\x00\x50\x00\x02'
471459
b'h2\x0Agoogle.comhttps://yahoo.com:8080'
472460
)
473461
payload_without_origin = (
474-
b'\x00\x15\x0A\x00\x00\x00\x00\x00'
462+
b'\x00\x00\x15\x0A\x00\x00\x00\x00\x00'
475463
b'\x00\x00\x00\x1D\x00\x50\x00\x02'
476464
b'h2\x0Agoogle.com'
477465
)
@@ -494,8 +482,7 @@ def test_altsvc_frame_with_origin_serializes_properly(self):
494482
assert s == self.payload_with_origin
495483

496484
def test_altsvc_frame_with_origin_parses_properly(self):
497-
f, length = Frame.parse_frame_header(self.payload_with_origin[:8])
498-
f.parse_body(memoryview(self.payload_with_origin[8:8 + length]))
485+
f = decode_frame(self.payload_with_origin)
499486

500487
assert isinstance(f, AltSvcFrame)
501488
assert f.host == b'google.com'
@@ -515,8 +502,7 @@ def test_altsvc_frame_without_origin_serializes_properly(self):
515502
assert s == self.payload_without_origin
516503

517504
def test_altsvc_frame_without_origin_parses_properly(self):
518-
f, length = Frame.parse_frame_header(self.payload_without_origin[:8])
519-
f.parse_body(memoryview(self.payload_without_origin[8:8 + length]))
505+
f = decode_frame(self.payload_without_origin)
520506

521507
assert isinstance(f, AltSvcFrame)
522508
assert f.host == b'google.com'
@@ -548,12 +534,11 @@ def test_blocked_serializes_properly(self):
548534
f = BlockedFrame(2)
549535

550536
s = f.serialize()
551-
assert s == b'\x00\x00\x0B\x00\x00\x00\x00\x02'
537+
assert s == b'\x00\x00\x00\x0B\x00\x00\x00\x00\x02'
552538

553539
def test_blocked_frame_parses_properly(self):
554-
s = b'\x00\x00\x0B\x00\x00\x00\x00\x02'
555-
f, length = Frame.parse_frame_header(s[:8])
556-
f.parse_body(memoryview(s[8:8 + length]))
540+
s = b'\x00\x00\x00\x0B\x00\x00\x00\x00\x02'
541+
f = decode_frame(s)
557542

558543
assert isinstance(f, BlockedFrame)
559544
assert f.flags == set()
@@ -1074,7 +1059,7 @@ def test_that_we_correctly_send_over_the_socket(self):
10741059

10751060
def test_we_can_read_from_the_socket(self):
10761061
sock = DummySocket()
1077-
sock.buffer = BytesIO(b'\x00\x08\x00\x01\x00\x00\x00\x01testdata')
1062+
sock.buffer = BytesIO(b'\x00\x00\x08\x00\x01\x00\x00\x00\x01testdata')
10781063

10791064
c = HTTP20Connection('www.google.com')
10801065
c._sock = sock

test/test_integration.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@
3131
hyper.http20.tls._context.verify_mode = ssl.CERT_NONE
3232

3333
def decode_frame(frame_data):
34-
f, length = Frame.parse_frame_header(frame_data[:8])
35-
f.parse_body(memoryview(frame_data[8:8 + length]))
36-
assert 8 + length == len(frame_data)
34+
f, length = Frame.parse_frame_header(frame_data[:9])
35+
f.parse_body(memoryview(frame_data[9:9 + length]))
36+
assert 9 + length == len(frame_data)
3737
return f
3838

3939

0 commit comments

Comments
 (0)