1# -*- coding: utf-8 -*- 2"""Hypertext Transfer Protocol Version 2.""" 3 4import struct 5import codecs 6 7from . import dpkt 8 9 10HTTP2_PREFACE = b'\x50\x52\x49\x20\x2a\x20\x48\x54\x54\x50\x2f\x32\x2e\x30\x0d\x0a\x0d\x0a\x53\x4d\x0d\x0a\x0d\x0a' 11 12# Frame types 13HTTP2_FRAME_DATA = 0 14HTTP2_FRAME_HEADERS = 1 15HTTP2_FRAME_PRIORITY = 2 16HTTP2_FRAME_RST_STREAM = 3 17HTTP2_FRAME_SETTINGS = 4 18HTTP2_FRAME_PUSH_PROMISE = 5 19HTTP2_FRAME_PING = 6 20HTTP2_FRAME_GOAWAY = 7 21HTTP2_FRAME_WINDOW_UPDATE = 8 22HTTP2_FRAME_CONTINUATION = 9 23 24# Flags 25HTTP2_FLAG_END_STREAM = 0x01 # for DATA and HEADERS frames 26HTTP2_FLAG_ACK = 0x01 # for SETTINGS and PING frames 27HTTP2_FLAG_END_HEADERS = 0x04 28HTTP2_FLAG_PADDED = 0x08 29HTTP2_FLAG_PRIORITY = 0x20 30 31# Settings 32HTTP2_SETTINGS_HEADER_TABLE_SIZE = 0x1 33HTTP2_SETTINGS_ENABLE_PUSH = 0x2 34HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS = 0x3 35HTTP2_SETTINGS_INITIAL_WINDOW_SIZE = 0x4 36HTTP2_SETTINGS_MAX_FRAME_SIZE = 0x5 37HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE = 0x6 38 39# Error codes 40HTTP2_NO_ERROR = 0x0 41HTTP2_PROTOCOL_ERROR = 0x1 42HTTP2_INTERNAL_ERROR = 0x2 43HTTP2_FLOW_CONTROL_ERROR = 0x3 44HTTP2_SETTINGS_TIMEOUT = 0x4 45HTTP2_STREAM_CLOSED = 0x5 46HTTP2_FRAME_SIZE_ERROR = 0x6 47HTTP2_REFUSED_STREAM = 0x7 48HTTP2_CANCEL = 0x8 49HTTP2_COMPRESSION_ERROR = 0x9 50HTTP2_CONNECT_ERROR = 0xa 51HTTP2_ENHANCE_YOUR_CALM = 0xb 52HTTP2_INADEQUATE_SECURITY = 0xc 53HTTP2_HTTP_1_1_REQUIRED = 0xd 54 55error_code_str = { 56 HTTP2_NO_ERROR: 'NO_ERROR', 57 HTTP2_PROTOCOL_ERROR: 'PROTOCOL_ERROR', 58 HTTP2_INTERNAL_ERROR: 'INTERNAL_ERROR', 59 HTTP2_FLOW_CONTROL_ERROR: 'FLOW_CONTROL_ERROR', 60 HTTP2_SETTINGS_TIMEOUT: 'SETTINGS_TIMEOUT', 61 HTTP2_STREAM_CLOSED: 'STREAM_CLOSED', 62 HTTP2_FRAME_SIZE_ERROR: 'FRAME_SIZE_ERROR', 63 HTTP2_REFUSED_STREAM: 'REFUSED_STREAM', 64 HTTP2_CANCEL: 'CANCEL', 65 HTTP2_COMPRESSION_ERROR: 'COMPRESSION_ERROR', 66 HTTP2_CONNECT_ERROR: 'CONNECT_ERROR', 67 HTTP2_ENHANCE_YOUR_CALM: 'ENHANCE_YOUR_CALM', 68 HTTP2_INADEQUATE_SECURITY: 'INADEQUATE_SECURITY', 69 HTTP2_HTTP_1_1_REQUIRED: 'HTTP_1_1_REQUIRED', 70} 71 72 73class HTTP2Exception(Exception): 74 pass 75 76 77class Preface(dpkt.Packet): 78 __hdr__ = ( 79 ('preface', '24s', HTTP2_PREFACE), 80 ) 81 82 def unpack(self, buf): 83 dpkt.Packet.unpack(self, buf) 84 if self.preface != HTTP2_PREFACE: 85 raise HTTP2Exception('Invalid HTTP/2 preface') 86 self.data = '' 87 88 89class Frame(dpkt.Packet): 90 """ 91 An HTTP/2 frame as defined in RFC 7540 92 """ 93 94 # struct.unpack can't handle the 3-byte int, so we parse it as bytes 95 # (and store it as bytes so dpkt doesn't get confused), and turn it into 96 # an int in a user-facing property 97 __hdr__ = ( 98 ('length_bytes', '3s', 0), 99 ('type', 'B', 0), 100 ('flags', 'B', 0), 101 ('stream_id', 'I', 0), 102 ) 103 104 def unpack(self, buf): 105 dpkt.Packet.unpack(self, buf) 106 # only take the right number of bytes 107 self.data = self.data[:self.length] 108 if len(self.data) != self.length: 109 raise dpkt.NeedData 110 111 @property 112 def length(self): 113 return struct.unpack('!I', b'\x00' + self.length_bytes)[0] 114 115 116class Priority(dpkt.Packet): 117 """ 118 Payload of a PRIORITY frame, also used in HEADERS frame with FLAG_PRIORITY. 119 120 Also used in the HEADERS frame if the PRIORITY flag is set. 121 """ 122 123 __hdr__ = ( 124 ('stream_dep', 'I', 0), 125 ('weight', 'B', 0), 126 ) 127 128 def unpack(self, buf): 129 dpkt.Packet.unpack(self, buf) 130 if len(self.data) != 0: 131 raise HTTP2Exception('Invalid number of bytes in PRIORITY frame') 132 self.exclusive = (self.stream_dep & 0x80000000) != 0 133 self.stream_dep &= 0x7fffffff 134 self.weight += 1 135 136 137class Setting(dpkt.Packet): 138 """ 139 A key-value pair used in the SETTINGS frame. 140 """ 141 142 __hdr__ = ( 143 ('identifier', 'H', 0), 144 ('value', 'I', 0), 145 ) 146 147 148class PaddedFrame(Frame): 149 """ 150 Abstract class for frame types that support the FLAG_PADDED flag: DATA, 151 HEADERS and PUSH_PROMISE. 152 """ 153 154 def unpack(self, buf): 155 Frame.unpack(self, buf) 156 if self.flags & HTTP2_FLAG_PADDED: 157 if self.length == 0: 158 raise HTTP2Exception('Missing padding length in PADDED frame') 159 self.pad_length = struct.unpack('B', self.data[0:1])[0] 160 if self.length <= self.pad_length: 161 raise HTTP2Exception('Missing padding bytes in PADDED frame') 162 self.unpadded_data = self.data[1:-self.pad_length] 163 else: 164 self.unpadded_data = self.data 165 166 167class DataFrame(PaddedFrame): 168 """ 169 Frame of type DATA. 170 """ 171 172 @property 173 def payload(self): 174 return self.unpadded_data 175 176 177class HeadersFrame(PaddedFrame): 178 """ 179 Frame of type HEADERS. 180 """ 181 182 def unpack(self, buf): 183 PaddedFrame.unpack(self, buf) 184 if self.flags & HTTP2_FLAG_PRIORITY: 185 if len(self.unpadded_data) < 5: 186 raise HTTP2Exception('Missing stream dependency in HEADERS frame with PRIORITY flag') 187 self.priority = Priority(self.unpadded_data[:5]) 188 self.block_fragment = self.unpadded_data[5:] 189 else: 190 self.block_fragment = self.unpadded_data 191 192 193class PriorityFrame(Frame): 194 """ 195 Frame of type PRIORITY. 196 """ 197 198 def unpack(self, buf): 199 Frame.unpack(self, buf) 200 self.priority = Priority(self.data) 201 202 203class RSTStreamFrame(Frame): 204 """ 205 Frame of type RST_STREAM. 206 """ 207 208 def unpack(self, buf): 209 Frame.unpack(self, buf) 210 if self.length != 4: 211 raise HTTP2Exception('Invalid number of bytes in RST_STREAM frame (must be 4)') 212 self.error_code = struct.unpack('!I', self.data)[0] 213 214 215class SettingsFrame(Frame): 216 """ 217 Frame of type SETTINGS. 218 """ 219 220 def unpack(self, buf): 221 Frame.unpack(self, buf) 222 if self.length % 6 != 0: 223 raise HTTP2Exception('Invalid number of bytes in SETTINGS frame (must be multiple of 6)') 224 self.settings = [] 225 i = 0 226 while i < self.length: 227 self.settings.append(Setting(self.data[i:i + 6])) 228 i += 6 229 230 231class PushPromiseFrame(PaddedFrame): 232 """ 233 Frame of type PUSH_PROMISE. 234 """ 235 236 def unpack(self, buf): 237 PaddedFrame.unpack(self, buf) 238 if len(self.unpadded_data) < 4: 239 raise HTTP2Exception('Missing promised stream ID in PUSH_PROMISE frame') 240 self.promised_id = struct.unpack('!I', self.data[:4])[0] 241 self.block_fragment = self.unpadded_data[4:] 242 243 244class PingFrame(Frame): 245 """ 246 Frame of type PING. 247 """ 248 249 def unpack(self, buf): 250 Frame.unpack(self, buf) 251 if self.length != 8: 252 raise HTTP2Exception('Invalid number of bytes in PING frame (must be 8)') 253 254 255class GoAwayFrame(Frame): 256 """ 257 Frame of type GO_AWAY. 258 """ 259 260 def unpack(self, buf): 261 Frame.unpack(self, buf) 262 if self.length < 8: 263 raise HTTP2Exception('Invalid number of bytes in GO_AWAY frame') 264 self.last_stream_id = struct.unpack('!I', self.data[:4])[0] 265 self.error_code = struct.unpack('!I', self.data[4:8])[0] 266 self.debug_data = self.data[8:] 267 268 269class WindowUpdateFrame(Frame): 270 """ 271 Frame of type WINDOW_UPDATE. 272 """ 273 274 def unpack(self, buf): 275 Frame.unpack(self, buf) 276 if self.length != 4: 277 raise HTTP2Exception('Invalid number of bytes in WINDOW_UPDATE frame (must be 4)') 278 self.window_increment = struct.unpack('!I', self.data)[0] 279 280 281class ContinuationFrame(Frame): 282 """ 283 Frame of type CONTINUATION. 284 """ 285 286 def unpack(self, buf): 287 Frame.unpack(self, buf) 288 self.block_fragment = self.data 289 290 291FRAME_TYPES = { 292 HTTP2_FRAME_DATA: ('DATA', DataFrame), 293 HTTP2_FRAME_HEADERS: ('HEADERS', HeadersFrame), 294 HTTP2_FRAME_PRIORITY: ('PRIORITY', PriorityFrame), 295 HTTP2_FRAME_RST_STREAM: ('RST_STREAM', RSTStreamFrame), 296 HTTP2_FRAME_SETTINGS: ('SETTINGS', SettingsFrame), 297 HTTP2_FRAME_PUSH_PROMISE: ('PUSH_PROMISE', PushPromiseFrame), 298 HTTP2_FRAME_PING: ('PING', PingFrame), 299 HTTP2_FRAME_GOAWAY: ('GOAWAY', GoAwayFrame), 300 HTTP2_FRAME_WINDOW_UPDATE: ('WINDOW_UPDATE', WindowUpdateFrame), 301 HTTP2_FRAME_CONTINUATION: ('CONTINUATION', ContinuationFrame), 302} 303 304 305class FrameFactory(object): 306 def __new__(cls, buf): 307 if len(buf) < 4: 308 raise dpkt.NeedData 309 t = struct.unpack('B', buf[3:4])[0] 310 frame_type = FRAME_TYPES.get(t, None) 311 if frame_type is None: 312 raise HTTP2Exception('Invalid frame type: ' + hex(t)) 313 return frame_type[1](buf) 314 315 316def frame_multi_factory(buf, preface=False): 317 """ 318 Attempt to parse one or more Frame's out of buf 319 320 Args: 321 buf: string containing HTTP/2 frames. May have an incomplete frame at the 322 end. 323 preface: expect an HTTP/2 preface at the beginning of the buffer. 324 325 Returns: 326 [Frame] 327 int, total bytes consumed, != len(buf) if an incomplete frame was left at 328 the end. 329 """ 330 i = 0 331 n = len(buf) 332 frames = [] 333 334 if preface: 335 try: 336 p = Preface(buf) 337 i += len(p) 338 except dpkt.NeedData: 339 return [], 0 340 341 while i < n: 342 try: 343 frame = FrameFactory(buf[i:]) 344 frames.append(frame) 345 i += len(frame) 346 except dpkt.NeedData: 347 break 348 return frames, i 349 350 351class TestFrame(object): 352 """Some data found in real traffic""" 353 354 @classmethod 355 def setup_class(cls): 356 # First TLS AppData record sent by Firefox (decrypted) 357 record = codecs.decode(b'505249202a20485454502f322e300d0a' 358 b'0d0a534d0d0a0d0a00000c0400000000' 359 b'00000400020000000500004000000004' 360 b'08000000000000bf0001000005020000' 361 b'00000300000000c80000050200000000' 362 b'05000000006400000502000000000700' 363 b'00000000000005020000000009000000' 364 b'070000000502000000000b0000000300', 'hex') 365 cls.frames, cls.i = frame_multi_factory(record, preface=True) 366 367 def test_frame(self): 368 import pytest 369 # Too short 370 pytest.raises(dpkt.NeedData, Frame, codecs.decode(b'000001' # length 371 b'0000' # type, flags 372 b'deadbeef', # stream id 373 'hex')) 374 375 def test_data(self): 376 # Padded DATA frame 377 frame_data_padded = FrameFactory(codecs.decode(b'000008' # length 378 b'0008' # type, flags 379 b'12345678' # stream id 380 b'05' # pad length 381 b'abcd' # data 382 b'1122334455', # padding 383 'hex')) 384 assert (frame_data_padded.length == 8) 385 assert (frame_data_padded.type == HTTP2_FRAME_DATA) 386 assert (frame_data_padded.flags == HTTP2_FLAG_PADDED) 387 assert (frame_data_padded.stream_id == 0x12345678) 388 assert (frame_data_padded.data == b'\x05\xAB\xCD\x11\x22\x33\x44\x55') 389 assert (frame_data_padded.pad_length == 5) 390 assert (frame_data_padded.unpadded_data == b'\xAB\xCD') 391 assert (frame_data_padded.payload == b'\xAB\xCD') 392 393 # empty DATA frame 394 frame_data_empty_end = FrameFactory(codecs.decode(b'000000' # length 395 b'0001' # type, flags 396 b'deadbeef', # stream id 397 'hex')) 398 assert (frame_data_empty_end.length == 0) 399 assert (frame_data_empty_end.type == HTTP2_FRAME_DATA) 400 assert (frame_data_empty_end.flags == HTTP2_FLAG_END_STREAM) 401 assert (frame_data_empty_end.stream_id == 0xdeadbeef) 402 assert (frame_data_empty_end.data == b'') 403 assert (frame_data_empty_end.unpadded_data == b'') 404 assert (frame_data_empty_end.payload == b'') 405 406 import pytest 407 # Invalid padding 408 with pytest.raises(HTTP2Exception) as e: 409 DataFrame(codecs.decode(b'000000' # length 410 b'0008' # type, flags 411 b'12345678' # stream id 412 b'', # missing padding 413 'hex')) 414 assert (str(e.value) == 'Missing padding length in PADDED frame') 415 416 with pytest.raises(HTTP2Exception) as e: 417 DataFrame(codecs.decode(b'000001' # length 418 b'0008' # type, flags 419 b'12345678' # stream id 420 b'01' 421 b'', # missing padding bytes 422 'hex')) 423 assert (str(e.value) == 'Missing padding bytes in PADDED frame') 424 425 def test_headers(self): 426 frame_headers = FrameFactory(codecs.decode(b'000003' # length 427 b'0100' # type, flags 428 b'deadbeef' # stream id 429 b'f00baa', # block fragment 430 'hex')) 431 assert (frame_headers.length == 3) 432 assert (frame_headers.type == HTTP2_FRAME_HEADERS) 433 assert (frame_headers.flags == 0) 434 assert (frame_headers.stream_id == 0xdeadbeef) 435 assert (frame_headers.data == b'\xF0\x0B\xAA') 436 assert (frame_headers.unpadded_data == b'\xF0\x0B\xAA') 437 assert (frame_headers.block_fragment == b'\xF0\x0B\xAA') 438 439 frame_headers_prio = FrameFactory(codecs.decode(b'000008' # length 440 b'0120' # type, flags 441 b'deadbeef' # stream id 442 b'cafebabe10' # priority 443 b'f00baa', # block fragment 444 'hex')) 445 assert (frame_headers_prio.length == 8) 446 assert (frame_headers_prio.type == HTTP2_FRAME_HEADERS) 447 assert (frame_headers_prio.flags == HTTP2_FLAG_PRIORITY) 448 assert (frame_headers_prio.stream_id == 0xdeadbeef) 449 assert (frame_headers_prio.data == b'\xCA\xFE\xBA\xBE\x10\xF0\x0B\xAA') 450 assert (frame_headers_prio.unpadded_data == b'\xCA\xFE\xBA\xBE\x10\xF0\x0B\xAA') 451 assert (frame_headers_prio.priority.exclusive is True) 452 assert (frame_headers_prio.priority.stream_dep == 0x4afebabe) 453 assert (frame_headers_prio.priority.weight == 0x11) 454 assert (frame_headers_prio.block_fragment == b'\xF0\x0B\xAA') 455 456 import pytest 457 # Invalid priority 458 with pytest.raises(HTTP2Exception) as e: 459 HeadersFrame(codecs.decode(b'000002' # length 460 b'0120' # type, flags 461 b'deadbeef' # stream id 462 b'1234', # invalid priority 463 'hex')) 464 assert (str(e.value) == 'Missing stream dependency in HEADERS frame with PRIORITY flag') 465 466 def test_priority(self): 467 frame_priority = FrameFactory(codecs.decode(b'000005' # length 468 b'0200' # type, flags 469 b'deadbeef' # stream id 470 b'cafebabe' # stream dep 471 b'12', # weight 472 'hex')) 473 assert (frame_priority.length == 5) 474 assert (frame_priority.type == HTTP2_FRAME_PRIORITY) 475 assert (frame_priority.flags == 0) 476 assert (frame_priority.stream_id == 0xdeadbeef) 477 assert (frame_priority.data == b'\xCA\xFE\xBA\xBE\x12') 478 assert (frame_priority.priority.data == b'') 479 assert (frame_priority.priority.exclusive is True) 480 assert (frame_priority.priority.stream_dep == 0x4afebabe) 481 assert (frame_priority.priority.weight == 0x13) 482 483 import pytest 484 # Invalid length 485 with pytest.raises(HTTP2Exception) as e: 486 PriorityFrame(codecs.decode(b'000006' # length 487 b'0200' # type, flags 488 b'deadbeef' # stream id 489 b'cafebabe' # stream dep 490 b'12' # weight 491 b'00', # unexpected additional payload 492 'hex')) 493 assert (str(e.value) == 'Invalid number of bytes in PRIORITY frame') 494 495 def test_rst_stream(self): 496 frame_rst = FrameFactory(codecs.decode(b'000004' # length 497 b'0300' # type, flags 498 b'deadbeef' # stream id 499 b'0000000c', # error code 500 'hex')) 501 assert (frame_rst.length == 4) 502 assert (frame_rst.type == HTTP2_FRAME_RST_STREAM) 503 assert (frame_rst.flags == 0) 504 assert (frame_rst.stream_id == 0xdeadbeef) 505 assert (frame_rst.data == b'\x00\x00\x00\x0c') 506 assert (frame_rst.error_code == HTTP2_INADEQUATE_SECURITY) 507 508 import pytest 509 # Invalid length 510 with pytest.raises(HTTP2Exception) as e: 511 RSTStreamFrame(codecs.decode(b'000005' # length 512 b'0300' # type, flags 513 b'deadbeef' # stream id 514 b'0000000c' # error code 515 b'00', # unexpected additional payload 516 'hex')) 517 assert (str(e.value) == 'Invalid number of bytes in RST_STREAM frame (must be 4)') 518 519 def test_settings(self): 520 frame_settings = FrameFactory(codecs.decode(b'00000c' # length 521 b'0400' # type, flags 522 b'00000000' # stream id 523 # settings 524 b'0004' # setting id 525 b'00020000' # setting value 526 b'0005' # setting id 527 b'00004000', # setting value 528 'hex')) 529 assert (frame_settings.length == 12) 530 assert (frame_settings.type == HTTP2_FRAME_SETTINGS) 531 assert (frame_settings.flags == 0) 532 assert (frame_settings.stream_id == 0) 533 assert (len(frame_settings.settings) == 2) 534 assert (frame_settings.settings[0].identifier == HTTP2_SETTINGS_INITIAL_WINDOW_SIZE) 535 assert (frame_settings.settings[0].value == 0x20000) 536 assert (frame_settings.settings[1].identifier == HTTP2_SETTINGS_MAX_FRAME_SIZE) 537 assert (frame_settings.settings[1].value == 0x4000) 538 539 # Settings ack, with empty payload 540 frame_settings_ack = FrameFactory(codecs.decode(b'000000' # length 541 b'0401' # type, flags 542 b'00000000', # stream id 543 'hex')) 544 assert (frame_settings_ack.length == 0) 545 assert (frame_settings_ack.type == HTTP2_FRAME_SETTINGS) 546 assert (frame_settings_ack.flags == HTTP2_FLAG_ACK) 547 assert (frame_settings_ack.stream_id == 0) 548 assert (len(frame_settings_ack.settings) == 0) 549 550 import pytest 551 # Invalid length 552 with pytest.raises(HTTP2Exception) as e: 553 SettingsFrame(codecs.decode(b'000005' # length 554 b'0400' # type, flags 555 b'deadbeef' # stream id 556 b'1234567890', # invalid length 557 'hex')) 558 assert (str(e.value) == 'Invalid number of bytes in SETTINGS frame (must be multiple of 6)') 559 560 def test_push_promise(self): 561 frame_pp = FrameFactory(codecs.decode(b'000007' # length 562 b'0500' # type, flags 563 b'deadbeef' # stream id 564 b'cafebabe' # promised id 565 b'123456', # some block fragment 566 'hex')) 567 assert (frame_pp.length == 7) 568 assert (frame_pp.type == HTTP2_FRAME_PUSH_PROMISE) 569 assert (frame_pp.flags == 0) 570 assert (frame_pp.stream_id == 0xdeadbeef) 571 assert (frame_pp.promised_id == 0xcafebabe) 572 assert (frame_pp.block_fragment == b'\x12\x34\x56') 573 574 import pytest 575 # Invalid length 576 with pytest.raises(HTTP2Exception) as e: 577 PushPromiseFrame(codecs.decode(b'000003' # length 578 b'0500' # type, flags 579 b'deadbeef' # stream id 580 b'cafeba', # missing promised id 581 'hex')) 582 assert (str(e.value) == 'Missing promised stream ID in PUSH_PROMISE frame') 583 584 def test_ping(self): 585 frame_ping = FrameFactory(codecs.decode(b'000008' # length 586 b'0600' # type, flags 587 b'deadbeef' # stream id 588 b'cafebabe12345678', # user data 589 'hex')) 590 assert (frame_ping.length == 8) 591 assert (frame_ping.type == HTTP2_FRAME_PING) 592 assert (frame_ping.flags == 0) 593 assert (frame_ping.stream_id == 0xdeadbeef) 594 assert (frame_ping.data == b'\xCA\xFE\xBA\xBE\x12\x34\x56\x78') 595 596 import pytest 597 # Invalid length 598 with pytest.raises(HTTP2Exception) as e: 599 PingFrame(codecs.decode(b'000005' # length 600 b'0600' # type, flags 601 b'deadbeef' # stream id 602 b'1234567890', # invalid length 603 'hex')) 604 assert (str(e.value) == 'Invalid number of bytes in PING frame (must be 8)') 605 606 def test_goaway(self): 607 frame_goaway = FrameFactory(codecs.decode(b'00000a' # length 608 b'0700' # type, flags 609 b'deadbeef' # stream id 610 b'00000000' # last stream id 611 b'00000000' # error code 612 b'cafe', # debug data 613 'hex')) 614 assert (frame_goaway.length == 10) 615 assert (frame_goaway.type == HTTP2_FRAME_GOAWAY) 616 assert (frame_goaway.flags == 0) 617 assert (frame_goaway.stream_id == 0xdeadbeef) 618 assert (frame_goaway.last_stream_id == 0) 619 assert (frame_goaway.error_code == HTTP2_NO_ERROR) 620 assert (frame_goaway.debug_data == b'\xCA\xFE') 621 622 import pytest 623 # Invalid length 624 with pytest.raises(HTTP2Exception) as e: 625 GoAwayFrame(codecs.decode(b'000005' # length 626 b'0700' # type, flags 627 b'deadbeef' # stream id 628 b'1234567890', # invalid length 629 'hex')) 630 assert (str(e.value) == 'Invalid number of bytes in GO_AWAY frame') 631 632 def test_window_update(self): 633 frame_wu = FrameFactory(codecs.decode(b'000004' # length 634 b'0800' # type, flags 635 b'deadbeef' # stream id 636 b'12345678', # window increment 637 'hex')) 638 assert (frame_wu.length == 4) 639 assert (frame_wu.type == HTTP2_FRAME_WINDOW_UPDATE) 640 assert (frame_wu.flags == 0) 641 assert (frame_wu.stream_id == 0xdeadbeef) 642 assert (frame_wu.window_increment == 0x12345678) 643 644 import pytest 645 # Invalid length 646 with pytest.raises(HTTP2Exception) as e: 647 WindowUpdateFrame(codecs.decode(b'000005' # length 648 b'0800' # type, flags 649 b'deadbeef' # stream id 650 b'1234567890', # invalid length 651 'hex')) 652 assert (str(e.value) == 'Invalid number of bytes in WINDOW_UPDATE frame (must be 4)') 653 654 def test_continuation(self): 655 frame_cont = FrameFactory(codecs.decode(b'000003' # length 656 b'0900' # type, flags 657 b'deadbeef' # stream id 658 b'f00baa', # block fragment 659 'hex')) 660 assert (frame_cont.length == 3) 661 assert (frame_cont.type == HTTP2_FRAME_CONTINUATION) 662 assert (frame_cont.flags == 0) 663 assert (frame_cont.stream_id == 0xdeadbeef) 664 assert (frame_cont.block_fragment == b'\xF0\x0B\xAA') 665 666 def test_factory(self): 667 import pytest 668 # Too short 669 pytest.raises(dpkt.NeedData, FrameFactory, codecs.decode(b'000000', 'hex')) 670 671 # Invalid type 672 with pytest.raises(HTTP2Exception) as e: 673 FrameFactory(codecs.decode(b'000000' # length 674 b'abcd' # type, flags 675 b'deadbeef', # stream id 676 'hex')) 677 assert (str(e.value) == 'Invalid frame type: 0xab') 678 679 def test_preface(self): 680 import pytest 681 # Preface 682 pytest.raises(dpkt.NeedData, Preface, 683 codecs.decode(b'505249202a20485454502f322e300d0a', 'hex')) 684 pytest.raises(dpkt.NeedData, Preface, b'\x00' * 23) 685 with pytest.raises(HTTP2Exception) as e: 686 Preface(b'\x00' * 24) 687 assert (str(e.value) == 'Invalid HTTP/2 preface') 688 689 def test_multi(self): 690 assert (self.i == 128) 691 assert (len(self.frames) == 7) 692 693 assert (self.frames[0].length == 12) 694 assert (self.frames[1].length == 4) 695 assert (self.frames[2].length == 5) 696 assert (self.frames[3].length == 5) 697 assert (self.frames[4].length == 5) 698 assert (self.frames[5].length == 5) 699 assert (self.frames[6].length == 5) 700 701 assert (self.frames[0].type == HTTP2_FRAME_SETTINGS) 702 assert (self.frames[1].type == HTTP2_FRAME_WINDOW_UPDATE) 703 assert (self.frames[2].type == HTTP2_FRAME_PRIORITY) 704 assert (self.frames[3].type == HTTP2_FRAME_PRIORITY) 705 assert (self.frames[4].type == HTTP2_FRAME_PRIORITY) 706 assert (self.frames[5].type == HTTP2_FRAME_PRIORITY) 707 assert (self.frames[6].type == HTTP2_FRAME_PRIORITY) 708 709 assert (self.frames[0].flags == 0) 710 assert (self.frames[1].flags == 0) 711 assert (self.frames[2].flags == 0) 712 assert (self.frames[3].flags == 0) 713 assert (self.frames[4].flags == 0) 714 assert (self.frames[5].flags == 0) 715 assert (self.frames[6].flags == 0) 716 717 assert (self.frames[0].stream_id == 0) 718 assert (self.frames[1].stream_id == 0) 719 assert (self.frames[2].stream_id == 3) 720 assert (self.frames[3].stream_id == 5) 721 assert (self.frames[4].stream_id == 7) 722 assert (self.frames[5].stream_id == 9) 723 assert (self.frames[6].stream_id == 11) 724 725 frames, i = frame_multi_factory( 726 codecs.decode(b'505249202a20485454502f322e300d0a', 'hex'), 727 preface=True) 728 assert (len(frames) == 0) 729 assert (i == 0) 730 731 # Only preface was parsed 732 frames, i = frame_multi_factory( 733 codecs.decode(b'505249202a20485454502f322e300d0a' 734 b'0d0a534d0d0a0d0a00000c0400000000', 'hex'), 735 preface=True) 736 assert (len(frames) == 0) 737 assert (i == 24) 738