1# -*- coding: utf-8 -*- 2""" 3hyperframe/frame 4~~~~~~~~~~~~~~~~ 5 6Defines framing logic for HTTP/2. Provides both classes to represent framed 7data and logic for aiding the connection when it comes to reading from the 8socket. 9""" 10import struct 11import binascii 12 13from .exceptions import ( 14 UnknownFrameError, InvalidPaddingError, InvalidFrameError 15) 16from .flags import Flag, Flags 17 18 19# The maximum initial length of a frame. Some frames have shorter maximum 20# lengths. 21FRAME_MAX_LEN = (2 ** 14) 22 23# The maximum allowed length of a frame. 24FRAME_MAX_ALLOWED_LEN = (2 ** 24) - 1 25 26# Stream association enumerations. 27_STREAM_ASSOC_HAS_STREAM = "has-stream" 28_STREAM_ASSOC_NO_STREAM = "no-stream" 29_STREAM_ASSOC_EITHER = "either" 30 31# Structs for packing and unpacking 32_STRUCT_HBBBL = struct.Struct(">HBBBL") 33_STRUCT_LL = struct.Struct(">LL") 34_STRUCT_HL = struct.Struct(">HL") 35_STRUCT_LB = struct.Struct(">LB") 36_STRUCT_L = struct.Struct(">L") 37_STRUCT_H = struct.Struct(">H") 38_STRUCT_B = struct.Struct(">B") 39 40 41class Frame(object): 42 """ 43 The base class for all HTTP/2 frames. 44 """ 45 #: The flags defined on this type of frame. 46 defined_flags = [] 47 48 #: The byte used to define the type of the frame. 49 type = None 50 51 # If 'has-stream', the frame's stream_id must be non-zero. If 'no-stream', 52 # it must be zero. If 'either', it's not checked. 53 stream_association = None 54 55 def __init__(self, stream_id, flags=()): 56 #: The stream identifier for the stream this frame was received on. 57 #: Set to 0 for frames sent on the connection (stream-id 0). 58 self.stream_id = stream_id 59 60 #: The flags set for this frame. 61 self.flags = Flags(self.defined_flags) 62 63 #: The frame length, excluding the nine-byte header. 64 self.body_len = 0 65 66 for flag in flags: 67 self.flags.add(flag) 68 69 if (not self.stream_id and 70 self.stream_association == _STREAM_ASSOC_HAS_STREAM): 71 raise ValueError('Stream ID must be non-zero') 72 if (self.stream_id and 73 self.stream_association == _STREAM_ASSOC_NO_STREAM): 74 raise ValueError('Stream ID must be zero') 75 76 def __repr__(self): 77 flags = ", ".join(self.flags) or "None" 78 body = binascii.hexlify(self.serialize_body()).decode('ascii') 79 if len(body) > 20: 80 body = body[:20] + "..." 81 return ( 82 "{type}(Stream: {stream}; Flags: {flags}): {body}" 83 ).format( 84 type=type(self).__name__, 85 stream=self.stream_id, 86 flags=flags, 87 body=body 88 ) 89 90 @staticmethod 91 def parse_frame_header(header, strict=False): 92 """ 93 Takes a 9-byte frame header and returns a tuple of the appropriate 94 Frame object and the length that needs to be read from the socket. 95 96 This populates the flags field, and determines how long the body is. 97 98 :param strict: Whether to raise an exception when encountering a frame 99 not defined by spec and implemented by hyperframe. 100 101 :raises hyperframe.exceptions.UnknownFrameError: If a frame of unknown 102 type is received. 103 104 .. versionchanged:: 5.0.0 105 Added :param:`strict` to accommodate :class:`ExtensionFrame` 106 """ 107 try: 108 fields = _STRUCT_HBBBL.unpack(header) 109 except struct.error: 110 raise InvalidFrameError("Invalid frame header") 111 112 # First 24 bits are frame length. 113 length = (fields[0] << 8) + fields[1] 114 type = fields[2] 115 flags = fields[3] 116 stream_id = fields[4] & 0x7FFFFFFF 117 118 try: 119 frame = FRAMES[type](stream_id) 120 except KeyError: 121 if strict: 122 raise UnknownFrameError(type, length) 123 frame = ExtensionFrame(type=type, stream_id=stream_id) 124 125 frame.parse_flags(flags) 126 return (frame, length) 127 128 def parse_flags(self, flag_byte): 129 for flag, flag_bit in self.defined_flags: 130 if flag_byte & flag_bit: 131 self.flags.add(flag) 132 133 return self.flags 134 135 def serialize(self): 136 """ 137 Convert a frame into a bytestring, representing the serialized form of 138 the frame. 139 """ 140 body = self.serialize_body() 141 self.body_len = len(body) 142 143 # Build the common frame header. 144 # First, get the flags. 145 flags = 0 146 147 for flag, flag_bit in self.defined_flags: 148 if flag in self.flags: 149 flags |= flag_bit 150 151 header = _STRUCT_HBBBL.pack( 152 (self.body_len >> 8) & 0xFFFF, # Length spread over top 24 bits 153 self.body_len & 0xFF, 154 self.type, 155 flags, 156 self.stream_id & 0x7FFFFFFF # Stream ID is 32 bits. 157 ) 158 159 return header + body 160 161 def serialize_body(self): 162 raise NotImplementedError() 163 164 def parse_body(self, data): 165 """ 166 Given the body of a frame, parses it into frame data. This populates 167 the non-header parts of the frame: that is, it does not populate the 168 stream ID or flags. 169 170 :param data: A memoryview object containing the body data of the frame. 171 Must not contain *more* data than the length returned by 172 :meth:`parse_frame_header 173 <hyperframe.frame.Frame.parse_frame_header>`. 174 """ 175 raise NotImplementedError() 176 177 178class Padding(object): 179 """ 180 Mixin for frames that contain padding. Defines extra fields that can be 181 used and set by frames that can be padded. 182 """ 183 def __init__(self, stream_id, pad_length=0, **kwargs): 184 super(Padding, self).__init__(stream_id, **kwargs) 185 186 #: The length of the padding to use. 187 self.pad_length = pad_length 188 189 def serialize_padding_data(self): 190 if 'PADDED' in self.flags: 191 return _STRUCT_B.pack(self.pad_length) 192 return b'' 193 194 def parse_padding_data(self, data): 195 if 'PADDED' in self.flags: 196 try: 197 self.pad_length = struct.unpack('!B', data[:1])[0] 198 except struct.error: 199 raise InvalidFrameError("Invalid Padding data") 200 return 1 201 return 0 202 203 @property 204 def total_padding(self): 205 return self.pad_length 206 207 208class Priority(object): 209 """ 210 Mixin for frames that contain priority data. Defines extra fields that can 211 be used and set by frames that contain priority data. 212 """ 213 def __init__(self, 214 stream_id, 215 depends_on=0x0, 216 stream_weight=0x0, 217 exclusive=False, 218 **kwargs): 219 super(Priority, self).__init__(stream_id, **kwargs) 220 221 #: The stream ID of the stream on which this stream depends. 222 self.depends_on = depends_on 223 224 #: The weight of the stream. This is an integer between 0 and 256. 225 self.stream_weight = stream_weight 226 227 #: Whether the exclusive bit was set. 228 self.exclusive = exclusive 229 230 def serialize_priority_data(self): 231 return _STRUCT_LB.pack( 232 self.depends_on + (0x80000000 if self.exclusive else 0), 233 self.stream_weight 234 ) 235 236 def parse_priority_data(self, data): 237 try: 238 self.depends_on, self.stream_weight = _STRUCT_LB.unpack(data[:5]) 239 except struct.error: 240 raise InvalidFrameError("Invalid Priority data") 241 242 self.exclusive = True if self.depends_on >> 31 else False 243 self.depends_on &= 0x7FFFFFFF 244 return 5 245 246 247class DataFrame(Padding, Frame): 248 """ 249 DATA frames convey arbitrary, variable-length sequences of octets 250 associated with a stream. One or more DATA frames are used, for instance, 251 to carry HTTP request or response payloads. 252 """ 253 #: The flags defined for DATA frames. 254 defined_flags = [ 255 Flag('END_STREAM', 0x01), 256 Flag('PADDED', 0x08), 257 ] 258 259 #: The type byte for data frames. 260 type = 0x0 261 262 stream_association = _STREAM_ASSOC_HAS_STREAM 263 264 def __init__(self, stream_id, data=b'', **kwargs): 265 super(DataFrame, self).__init__(stream_id, **kwargs) 266 267 #: The data contained on this frame. 268 self.data = data 269 270 def serialize_body(self): 271 padding_data = self.serialize_padding_data() 272 padding = b'\0' * self.total_padding 273 if isinstance(self.data, memoryview): 274 self.data = self.data.tobytes() 275 return b''.join([padding_data, self.data, padding]) 276 277 def parse_body(self, data): 278 padding_data_length = self.parse_padding_data(data) 279 self.data = ( 280 data[padding_data_length:len(data)-self.total_padding].tobytes() 281 ) 282 self.body_len = len(data) 283 284 if self.total_padding and self.total_padding >= self.body_len: 285 raise InvalidPaddingError("Padding is too long.") 286 287 @property 288 def flow_controlled_length(self): 289 """ 290 The length of the frame that needs to be accounted for when considering 291 flow control. 292 """ 293 padding_len = 0 294 if 'PADDED' in self.flags: 295 # Account for extra 1-byte padding length field, which is still 296 # present if possibly zero-valued. 297 padding_len = self.total_padding + 1 298 return len(self.data) + padding_len 299 300 301class PriorityFrame(Priority, Frame): 302 """ 303 The PRIORITY frame specifies the sender-advised priority of a stream. It 304 can be sent at any time for an existing stream. This enables 305 reprioritisation of existing streams. 306 """ 307 #: The flags defined for PRIORITY frames. 308 defined_flags = [] 309 310 #: The type byte defined for PRIORITY frames. 311 type = 0x02 312 313 stream_association = _STREAM_ASSOC_HAS_STREAM 314 315 def serialize_body(self): 316 return self.serialize_priority_data() 317 318 def parse_body(self, data): 319 self.parse_priority_data(data) 320 self.body_len = len(data) 321 322 323class RstStreamFrame(Frame): 324 """ 325 The RST_STREAM frame allows for abnormal termination of a stream. When sent 326 by the initiator of a stream, it indicates that they wish to cancel the 327 stream or that an error condition has occurred. When sent by the receiver 328 of a stream, it indicates that either the receiver is rejecting the stream, 329 requesting that the stream be cancelled or that an error condition has 330 occurred. 331 """ 332 #: The flags defined for RST_STREAM frames. 333 defined_flags = [] 334 335 #: The type byte defined for RST_STREAM frames. 336 type = 0x03 337 338 stream_association = _STREAM_ASSOC_HAS_STREAM 339 340 def __init__(self, stream_id, error_code=0, **kwargs): 341 super(RstStreamFrame, self).__init__(stream_id, **kwargs) 342 343 #: The error code used when resetting the stream. 344 self.error_code = error_code 345 346 def serialize_body(self): 347 return _STRUCT_L.pack(self.error_code) 348 349 def parse_body(self, data): 350 if len(data) != 4: 351 raise InvalidFrameError( 352 "RST_STREAM must have 4 byte body: actual length %s." % 353 len(data) 354 ) 355 356 try: 357 self.error_code = _STRUCT_L.unpack(data)[0] 358 except struct.error: # pragma: no cover 359 raise InvalidFrameError("Invalid RST_STREAM body") 360 361 self.body_len = 4 362 363 364class SettingsFrame(Frame): 365 """ 366 The SETTINGS frame conveys configuration parameters that affect how 367 endpoints communicate. The parameters are either constraints on peer 368 behavior or preferences. 369 370 Settings are not negotiated. Settings describe characteristics of the 371 sending peer, which are used by the receiving peer. Different values for 372 the same setting can be advertised by each peer. For example, a client 373 might set a high initial flow control window, whereas a server might set a 374 lower value to conserve resources. 375 """ 376 #: The flags defined for SETTINGS frames. 377 defined_flags = [Flag('ACK', 0x01)] 378 379 #: The type byte defined for SETTINGS frames. 380 type = 0x04 381 382 stream_association = _STREAM_ASSOC_NO_STREAM 383 384 # We need to define the known settings, they may as well be class 385 # attributes. 386 #: The byte that signals the SETTINGS_HEADER_TABLE_SIZE setting. 387 HEADER_TABLE_SIZE = 0x01 388 #: The byte that signals the SETTINGS_ENABLE_PUSH setting. 389 ENABLE_PUSH = 0x02 390 #: The byte that signals the SETTINGS_MAX_CONCURRENT_STREAMS setting. 391 MAX_CONCURRENT_STREAMS = 0x03 392 #: The byte that signals the SETTINGS_INITIAL_WINDOW_SIZE setting. 393 INITIAL_WINDOW_SIZE = 0x04 394 #: The byte that signals the SETTINGS_MAX_FRAME_SIZE setting. 395 MAX_FRAME_SIZE = 0x05 396 #: The byte that signals the SETTINGS_MAX_HEADER_LIST_SIZE setting. 397 MAX_HEADER_LIST_SIZE = 0x06 398 #: The byte that signals SETTINGS_ENABLE_CONNECT_PROTOCOL setting. 399 ENABLE_CONNECT_PROTOCOL = 0x08 400 401 def __init__(self, stream_id=0, settings=None, **kwargs): 402 super(SettingsFrame, self).__init__(stream_id, **kwargs) 403 404 if settings and "ACK" in kwargs.get("flags", ()): 405 raise ValueError("Settings must be empty if ACK flag is set.") 406 407 #: A dictionary of the setting type byte to the value of the setting. 408 self.settings = settings or {} 409 410 def serialize_body(self): 411 return b''.join([_STRUCT_HL.pack(setting & 0xFF, value) 412 for setting, value in self.settings.items()]) 413 414 def parse_body(self, data): 415 body_len = 0 416 for i in range(0, len(data), 6): 417 try: 418 name, value = _STRUCT_HL.unpack(data[i:i+6]) 419 except struct.error: 420 raise InvalidFrameError("Invalid SETTINGS body") 421 422 self.settings[name] = value 423 body_len += 6 424 425 self.body_len = body_len 426 427 428class PushPromiseFrame(Padding, Frame): 429 """ 430 The PUSH_PROMISE frame is used to notify the peer endpoint in advance of 431 streams the sender intends to initiate. 432 """ 433 #: The flags defined for PUSH_PROMISE frames. 434 defined_flags = [ 435 Flag('END_HEADERS', 0x04), 436 Flag('PADDED', 0x08) 437 ] 438 439 #: The type byte defined for PUSH_PROMISE frames. 440 type = 0x05 441 442 stream_association = _STREAM_ASSOC_HAS_STREAM 443 444 def __init__(self, stream_id, promised_stream_id=0, data=b'', **kwargs): 445 super(PushPromiseFrame, self).__init__(stream_id, **kwargs) 446 447 #: The stream ID that is promised by this frame. 448 self.promised_stream_id = promised_stream_id 449 450 #: The HPACK-encoded header block for the simulated request on the new 451 #: stream. 452 self.data = data 453 454 def serialize_body(self): 455 padding_data = self.serialize_padding_data() 456 padding = b'\0' * self.total_padding 457 data = _STRUCT_L.pack(self.promised_stream_id) 458 return b''.join([padding_data, data, self.data, padding]) 459 460 def parse_body(self, data): 461 padding_data_length = self.parse_padding_data(data) 462 463 try: 464 self.promised_stream_id = _STRUCT_L.unpack( 465 data[padding_data_length:padding_data_length + 4] 466 )[0] 467 except struct.error: 468 raise InvalidFrameError("Invalid PUSH_PROMISE body") 469 470 self.data = data[padding_data_length + 4:].tobytes() 471 self.body_len = len(data) 472 473 if self.total_padding and self.total_padding >= self.body_len: 474 raise InvalidPaddingError("Padding is too long.") 475 476 477class PingFrame(Frame): 478 """ 479 The PING frame is a mechanism for measuring a minimal round-trip time from 480 the sender, as well as determining whether an idle connection is still 481 functional. PING frames can be sent from any endpoint. 482 """ 483 #: The flags defined for PING frames. 484 defined_flags = [Flag('ACK', 0x01)] 485 486 #: The type byte defined for PING frames. 487 type = 0x06 488 489 stream_association = _STREAM_ASSOC_NO_STREAM 490 491 def __init__(self, stream_id=0, opaque_data=b'', **kwargs): 492 super(PingFrame, self).__init__(stream_id, **kwargs) 493 494 #: The opaque data sent in this PING frame, as a bytestring. 495 self.opaque_data = opaque_data 496 497 def serialize_body(self): 498 if len(self.opaque_data) > 8: 499 raise InvalidFrameError( 500 "PING frame may not have more than 8 bytes of data, got %s" % 501 self.opaque_data 502 ) 503 504 data = self.opaque_data 505 data += b'\x00' * (8 - len(self.opaque_data)) 506 return data 507 508 def parse_body(self, data): 509 if len(data) != 8: 510 raise InvalidFrameError( 511 "PING frame must have 8 byte length: got %s" % len(data) 512 ) 513 514 self.opaque_data = data.tobytes() 515 self.body_len = 8 516 517 518class GoAwayFrame(Frame): 519 """ 520 The GOAWAY frame informs the remote peer to stop creating streams on this 521 connection. It can be sent from the client or the server. Once sent, the 522 sender will ignore frames sent on new streams for the remainder of the 523 connection. 524 """ 525 #: The flags defined for GOAWAY frames. 526 defined_flags = [] 527 528 #: The type byte defined for GOAWAY frames. 529 type = 0x07 530 531 stream_association = _STREAM_ASSOC_NO_STREAM 532 533 def __init__(self, 534 stream_id=0, 535 last_stream_id=0, 536 error_code=0, 537 additional_data=b'', 538 **kwargs): 539 super(GoAwayFrame, self).__init__(stream_id, **kwargs) 540 541 #: The last stream ID definitely seen by the remote peer. 542 self.last_stream_id = last_stream_id 543 544 #: The error code for connection teardown. 545 self.error_code = error_code 546 547 #: Any additional data sent in the GOAWAY. 548 self.additional_data = additional_data 549 550 def serialize_body(self): 551 data = _STRUCT_LL.pack( 552 self.last_stream_id & 0x7FFFFFFF, 553 self.error_code 554 ) 555 data += self.additional_data 556 557 return data 558 559 def parse_body(self, data): 560 try: 561 self.last_stream_id, self.error_code = _STRUCT_LL.unpack( 562 data[:8] 563 ) 564 except struct.error: 565 raise InvalidFrameError("Invalid GOAWAY body.") 566 567 self.body_len = len(data) 568 569 if len(data) > 8: 570 self.additional_data = data[8:].tobytes() 571 572 573class WindowUpdateFrame(Frame): 574 """ 575 The WINDOW_UPDATE frame is used to implement flow control. 576 577 Flow control operates at two levels: on each individual stream and on the 578 entire connection. 579 580 Both types of flow control are hop by hop; that is, only between the two 581 endpoints. Intermediaries do not forward WINDOW_UPDATE frames between 582 dependent connections. However, throttling of data transfer by any receiver 583 can indirectly cause the propagation of flow control information toward the 584 original sender. 585 """ 586 #: The flags defined for WINDOW_UPDATE frames. 587 defined_flags = [] 588 589 #: The type byte defined for WINDOW_UPDATE frames. 590 type = 0x08 591 592 stream_association = _STREAM_ASSOC_EITHER 593 594 def __init__(self, stream_id, window_increment=0, **kwargs): 595 super(WindowUpdateFrame, self).__init__(stream_id, **kwargs) 596 597 #: The amount the flow control window is to be incremented. 598 self.window_increment = window_increment 599 600 def serialize_body(self): 601 return _STRUCT_L.pack(self.window_increment & 0x7FFFFFFF) 602 603 def parse_body(self, data): 604 try: 605 self.window_increment = _STRUCT_L.unpack(data)[0] 606 except struct.error: 607 raise InvalidFrameError("Invalid WINDOW_UPDATE body") 608 609 self.body_len = 4 610 611 612class HeadersFrame(Padding, Priority, Frame): 613 """ 614 The HEADERS frame carries name-value pairs. It is used to open a stream. 615 HEADERS frames can be sent on a stream in the "open" or "half closed 616 (remote)" states. 617 618 The HeadersFrame class is actually basically a data frame in this 619 implementation, because of the requirement to control the sizes of frames. 620 A header block fragment that doesn't fit in an entire HEADERS frame needs 621 to be followed with CONTINUATION frames. From the perspective of the frame 622 building code the header block is an opaque data segment. 623 """ 624 #: The flags defined for HEADERS frames. 625 defined_flags = [ 626 Flag('END_STREAM', 0x01), 627 Flag('END_HEADERS', 0x04), 628 Flag('PADDED', 0x08), 629 Flag('PRIORITY', 0x20), 630 ] 631 632 #: The type byte defined for HEADERS frames. 633 type = 0x01 634 635 stream_association = _STREAM_ASSOC_HAS_STREAM 636 637 def __init__(self, stream_id, data=b'', **kwargs): 638 super(HeadersFrame, self).__init__(stream_id, **kwargs) 639 640 #: The HPACK-encoded header block. 641 self.data = data 642 643 def serialize_body(self): 644 padding_data = self.serialize_padding_data() 645 padding = b'\0' * self.total_padding 646 647 if 'PRIORITY' in self.flags: 648 priority_data = self.serialize_priority_data() 649 else: 650 priority_data = b'' 651 652 return b''.join([padding_data, priority_data, self.data, padding]) 653 654 def parse_body(self, data): 655 padding_data_length = self.parse_padding_data(data) 656 data = data[padding_data_length:] 657 658 if 'PRIORITY' in self.flags: 659 priority_data_length = self.parse_priority_data(data) 660 else: 661 priority_data_length = 0 662 663 self.body_len = len(data) 664 self.data = ( 665 data[priority_data_length:len(data)-self.total_padding].tobytes() 666 ) 667 668 if self.total_padding and self.total_padding >= self.body_len: 669 raise InvalidPaddingError("Padding is too long.") 670 671 672class ContinuationFrame(Frame): 673 """ 674 The CONTINUATION frame is used to continue a sequence of header block 675 fragments. Any number of CONTINUATION frames can be sent on an existing 676 stream, as long as the preceding frame on the same stream is one of 677 HEADERS, PUSH_PROMISE or CONTINUATION without the END_HEADERS flag set. 678 679 Much like the HEADERS frame, hyper treats this as an opaque data frame with 680 different flags and a different type. 681 """ 682 #: The flags defined for CONTINUATION frames. 683 defined_flags = [Flag('END_HEADERS', 0x04)] 684 685 #: The type byte defined for CONTINUATION frames. 686 type = 0x09 687 688 stream_association = _STREAM_ASSOC_HAS_STREAM 689 690 def __init__(self, stream_id, data=b'', **kwargs): 691 super(ContinuationFrame, self).__init__(stream_id, **kwargs) 692 693 #: The HPACK-encoded header block. 694 self.data = data 695 696 def serialize_body(self): 697 return self.data 698 699 def parse_body(self, data): 700 self.data = data.tobytes() 701 self.body_len = len(data) 702 703 704class AltSvcFrame(Frame): 705 """ 706 The ALTSVC frame is used to advertise alternate services that the current 707 host, or a different one, can understand. This frame is standardised as 708 part of RFC 7838. 709 710 This frame does no work to validate that the ALTSVC field parameter is 711 acceptable per the rules of RFC 7838. 712 713 .. note:: If the ``stream_id`` of this frame is nonzero, the origin field 714 must have zero length. Conversely, if the ``stream_id`` of this 715 frame is zero, the origin field must have nonzero length. Put 716 another way, a valid ALTSVC frame has ``stream_id != 0`` XOR 717 ``len(origin) != 0``. 718 """ 719 type = 0xA 720 721 stream_association = _STREAM_ASSOC_EITHER 722 723 def __init__(self, stream_id, origin=b'', field=b'', **kwargs): 724 super(AltSvcFrame, self).__init__(stream_id, **kwargs) 725 726 if not isinstance(origin, bytes): 727 raise ValueError("AltSvc origin must be bytestring.") 728 if not isinstance(field, bytes): 729 raise ValueError("AltSvc field must be a bytestring.") 730 self.origin = origin 731 self.field = field 732 733 def serialize_body(self): 734 origin_len = _STRUCT_H.pack(len(self.origin)) 735 return b''.join([origin_len, self.origin, self.field]) 736 737 def parse_body(self, data): 738 try: 739 origin_len = _STRUCT_H.unpack(data[0:2])[0] 740 self.origin = data[2:2+origin_len].tobytes() 741 742 if len(self.origin) != origin_len: 743 raise InvalidFrameError("Invalid ALTSVC frame body.") 744 745 self.field = data[2+origin_len:].tobytes() 746 except (struct.error, ValueError): 747 raise InvalidFrameError("Invalid ALTSVC frame body.") 748 749 self.body_len = len(data) 750 751 752class ExtensionFrame(Frame): 753 """ 754 ExtensionFrame is used to wrap frames which are not natively interpretable 755 by hyperframe. 756 757 Although certain byte prefixes are ordained by specification to have 758 certain contextual meanings, frames with other prefixes are not prohibited, 759 and may be used to communicate arbitrary meaning between HTTP/2 peers. 760 761 Thus, hyperframe, rather than raising an exception when such a frame is 762 encountered, wraps it in a generic frame to be properly acted upon by 763 upstream consumers which might have additional context on how to use it. 764 765 .. versionadded:: 5.0.0 766 """ 767 768 stream_association = _STREAM_ASSOC_EITHER 769 770 def __init__(self, type, stream_id, **kwargs): 771 super(ExtensionFrame, self).__init__(stream_id, **kwargs) 772 self.type = type 773 self.flag_byte = None 774 775 def parse_flags(self, flag_byte): 776 """ 777 For extension frames, we parse the flags by just storing a flag byte. 778 """ 779 self.flag_byte = flag_byte 780 781 def parse_body(self, data): 782 self.body = data.tobytes() 783 self.body_len = len(data) 784 785 def serialize(self): 786 """ 787 A broad override of the serialize method that ensures that the data 788 comes back out exactly as it came in. This should not be used in most 789 user code: it exists only as a helper method if frames need to be 790 reconstituted. 791 """ 792 # Build the frame header. 793 # First, get the flags. 794 flags = self.flag_byte 795 796 header = _STRUCT_HBBBL.pack( 797 (self.body_len >> 8) & 0xFFFF, # Length spread over top 24 bits 798 self.body_len & 0xFF, 799 self.type, 800 flags, 801 self.stream_id & 0x7FFFFFFF # Stream ID is 32 bits. 802 ) 803 804 return header + self.body 805 806 807_FRAME_CLASSES = [ 808 DataFrame, 809 HeadersFrame, 810 PriorityFrame, 811 RstStreamFrame, 812 SettingsFrame, 813 PushPromiseFrame, 814 PingFrame, 815 GoAwayFrame, 816 WindowUpdateFrame, 817 ContinuationFrame, 818 AltSvcFrame, 819] 820#: FRAMES maps the type byte for each frame to the class used to represent that 821#: frame. 822FRAMES = {cls.type: cls for cls in _FRAME_CLASSES} 823