Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
frame.py938 linesDownload Raw Back to hyperframe
1"""2Framing logic for HTTP/2.3 4Provides both classes to represent framed5data and logic for aiding the connection when it comes to reading from the6socket.7"""8from __future__ import annotations9 10import binascii11import struct12from typing import TYPE_CHECKING, Any13 14if TYPE_CHECKING:15    from collections.abc import Iterable  # pragma: no cover16 17from .exceptions import InvalidDataError, InvalidFrameError, InvalidPaddingError, UnknownFrameError18from .flags import Flag, Flags19 20# The maximum initial length of a frame. Some frames have shorter maximum21# lengths.22FRAME_MAX_LEN = (2 ** 14)23 24# The maximum allowed length of a frame.25FRAME_MAX_ALLOWED_LEN = (2 ** 24) - 126 27# Stream association enumerations.28_STREAM_ASSOC_HAS_STREAM = "has-stream"29_STREAM_ASSOC_NO_STREAM = "no-stream"30_STREAM_ASSOC_EITHER = "either"31 32# Structs for packing and unpacking33_STRUCT_HBBBL = struct.Struct(">HBBBL")34_STRUCT_LL = struct.Struct(">LL")35_STRUCT_HL = struct.Struct(">HL")36_STRUCT_LB = struct.Struct(">LB")37_STRUCT_L = struct.Struct(">L")38_STRUCT_H = struct.Struct(">H")39_STRUCT_B = struct.Struct(">B")40 41 42class Frame:43    """44    The base class for all HTTP/2 frames.45    """46 47    #: The flags defined on this type of frame.48    defined_flags: list[Flag] = []49 50    #: The byte used to define the type of the frame.51    type: int | None = None52 53    # If 'has-stream', the frame's stream_id must be non-zero. If 'no-stream',54    # it must be zero. If 'either', it's not checked.55    stream_association: str | None = None56 57    def __init__(self, stream_id: int, flags: Iterable[str] = ()) -> None:58        #: The stream identifier for the stream this frame was received on.59        #: Set to 0 for frames sent on the connection (stream-id 0).60        self.stream_id = stream_id61 62        #: The flags set for this frame.63        self.flags = Flags(self.defined_flags)64 65        #: The frame length, excluding the nine-byte header.66        self.body_len = 067 68        for flag in flags:69            self.flags.add(flag)70 71        if not self.stream_id and self.stream_association == _STREAM_ASSOC_HAS_STREAM:72            msg = f"Stream ID must be non-zero for {type(self).__name__}"73            raise InvalidDataError(msg)74        if self.stream_id and self.stream_association == _STREAM_ASSOC_NO_STREAM:75            msg = f"Stream ID must be zero for {type(self).__name__} with stream_id={self.stream_id}"76            raise InvalidDataError(msg)77 78    def __repr__(self) -> str:79        return (80            f"{type(self).__name__}(stream_id={self.stream_id}, flags={self.flags!r}): {self._body_repr()}"81        )82 83    def _body_repr(self) -> str:84        # More specific implementation may be provided by subclasses of Frame.85        # This fallback shows the serialized (and truncated) body content.86        return _raw_data_repr(self.serialize_body())87 88    @staticmethod89    def explain(data: memoryview) -> tuple[Frame, int]:90        """91        Takes a bytestring and tries to parse a single frame and print it.92 93        This function is only provided for debugging purposes.94 95        :param data: A memoryview object containing the raw data of at least96                     one complete frame (header and body).97 98        .. versionadded:: 6.0.099        """100        frame, length = Frame.parse_frame_header(data[:9])101        frame.parse_body(data[9:9 + length])102        print(frame)  # noqa: T201103        return frame, length104 105    @staticmethod106    def parse_frame_header(header: memoryview, strict: bool = False) -> tuple[Frame, int]:107        """108        Takes a 9-byte frame header and returns a tuple of the appropriate109        Frame object and the length that needs to be read from the socket.110 111        This populates the flags field, and determines how long the body is.112 113        :param header: A memoryview object containing the 9-byte frame header114                       data of a frame. Must not contain more or less.115 116        :param strict: Whether to raise an exception when encountering a frame117            not defined by spec and implemented by hyperframe.118 119        :raises hyperframe.exceptions.UnknownFrameError: If a frame of unknown120            type is received.121 122        .. versionchanged:: 5.0.0123            Added ``strict`` parameter to accommodate :class:`ExtensionFrame`124        """125        try:126            fields = _STRUCT_HBBBL.unpack(header)127        except struct.error as err:128            msg = "Invalid frame header"129            raise InvalidFrameError(msg) from err130 131        # First 24 bits are frame length.132        length = (fields[0] << 8) + fields[1]133        typ_e = fields[2]134        flags = fields[3]135        stream_id = fields[4] & 0x7FFFFFFF136 137        try:138            frame = FRAMES[typ_e](stream_id)139        except KeyError as err:140            if strict:141                raise UnknownFrameError(typ_e, length) from err142            frame = ExtensionFrame(type=typ_e, stream_id=stream_id)143 144        frame.parse_flags(flags)145        return (frame, length)146 147    def parse_flags(self, flag_byte: int) -> Flags:148        for flag, flag_bit in self.defined_flags:149            if flag_byte & flag_bit:150                self.flags.add(flag)151 152        return self.flags153 154    def serialize(self) -> bytes:155        """156        Convert a frame into a bytestring, representing the serialized form of157        the frame.158        """159        body = self.serialize_body()160        self.body_len = len(body)161 162        # Build the common frame header.163        # First, get the flags.164        flags = 0165 166        for flag, flag_bit in self.defined_flags:167            if flag in self.flags:168                flags |= flag_bit169 170        header = _STRUCT_HBBBL.pack(171            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits172            self.body_len & 0xFF,173            self.type,174            flags,175            self.stream_id & 0x7FFFFFFF,  # Stream ID is 32 bits.176        )177 178        return header + body179 180    def serialize_body(self) -> bytes:181        raise NotImplementedError182 183    def parse_body(self, data: memoryview) -> None:184        """185        Given the body of a frame, parses it into frame data. This populates186        the non-header parts of the frame: that is, it does not populate the187        stream ID or flags.188 189        :param data: A memoryview object containing the body data of the frame.190                     Must not contain *more* data than the length returned by191                     :meth:`parse_frame_header192                     <hyperframe.frame.Frame.parse_frame_header>`.193        """194        raise NotImplementedError195 196 197class Padding:198    """199    Mixin for frames that contain padding. Defines extra fields that can be200    used and set by frames that can be padded.201    """202 203    def __init__(self, stream_id: int, pad_length: int = 0, **kwargs: Any) -> None:204        super().__init__(stream_id, **kwargs)  # type: ignore205 206        #: The length of the padding to use.207        self.pad_length = pad_length208 209    def serialize_padding_data(self) -> bytes:210        if "PADDED" in self.flags:  # type: ignore211            return _STRUCT_B.pack(self.pad_length)212        return b""213 214    def parse_padding_data(self, data: memoryview) -> int:215        if "PADDED" in self.flags:  # type: ignore216            try:217                self.pad_length = struct.unpack("!B", data[:1])[0]218            except struct.error as err:219                msg = "Invalid Padding data"220                raise InvalidFrameError(msg) from err221            return 1222        return 0223 224    #: .. deprecated:: 5.2.1225    #:    Use self.pad_length instead.226    @property227    def total_padding(self) -> int:  # pragma: no cover228        import warnings229        warnings.warn(230            "total_padding contains the same information as pad_length.",231            DeprecationWarning,232            stacklevel=2,233        )234        return self.pad_length235 236 237class Priority:238    """239    Mixin for frames that contain priority data. Defines extra fields that can240    be used and set by frames that contain priority data.241    """242 243    def __init__(self,244                 stream_id: int,245                 depends_on: int = 0x0,246                 stream_weight: int = 0x0,247                 exclusive: bool = False,248                 **kwargs: Any) -> None:249        super().__init__(stream_id, **kwargs)  # type: ignore250 251        #: The stream ID of the stream on which this stream depends.252        self.depends_on = depends_on253 254        #: The weight of the stream. This is an integer between 0 and 256.255        self.stream_weight = stream_weight256 257        #: Whether the exclusive bit was set.258        self.exclusive = exclusive259 260    def serialize_priority_data(self) -> bytes:261        return _STRUCT_LB.pack(262            self.depends_on + (0x80000000 if self.exclusive else 0),263            self.stream_weight,264        )265 266    def parse_priority_data(self, data: memoryview) -> int:267        try:268            self.depends_on, self.stream_weight = _STRUCT_LB.unpack(data[:5])269        except struct.error as err:270            msg = "Invalid Priority data"271            raise InvalidFrameError(msg) from err272 273        self.exclusive = bool(self.depends_on >> 31)274        self.depends_on &= 0x7FFFFFFF275        return 5276 277 278class DataFrame(Padding, Frame):279    """280    DATA frames convey arbitrary, variable-length sequences of octets281    associated with a stream. One or more DATA frames are used, for instance,282    to carry HTTP request or response payloads.283    """284 285    #: The flags defined for DATA frames.286    defined_flags = [287        Flag("END_STREAM", 0x01),288        Flag("PADDED", 0x08),289    ]290 291    #: The type byte for data frames.292    type = 0x0293 294    stream_association = _STREAM_ASSOC_HAS_STREAM295 296    def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None:297        super().__init__(stream_id, **kwargs)298 299        #: The data contained on this frame.300        self.data = data301 302    def serialize_body(self) -> bytes:303        padding_data = self.serialize_padding_data()304        padding = b"\0" * self.pad_length305        if isinstance(self.data, memoryview):306            self.data = self.data.tobytes()307        return b"".join([padding_data, self.data, padding])308 309    def parse_body(self, data: memoryview) -> None:310        padding_data_length = self.parse_padding_data(data)311        self.data = (312            data[padding_data_length:len(data)-self.pad_length].tobytes()313        )314        self.body_len = len(data)315 316        if self.pad_length and self.pad_length >= self.body_len:317            msg = "Padding is too long."318            raise InvalidPaddingError(msg)319 320    @property321    def flow_controlled_length(self) -> int:322        """323        The length of the frame that needs to be accounted for when considering324        flow control.325        """326        padding_len = 0327        if "PADDED" in self.flags:328            # Account for extra 1-byte padding length field, which is still329            # present if possibly zero-valued.330            padding_len = self.pad_length + 1331        return len(self.data) + padding_len332 333 334class PriorityFrame(Priority, Frame):335    """336    The PRIORITY frame specifies the sender-advised priority of a stream. It337    can be sent at any time for an existing stream. This enables338    reprioritisation of existing streams.339    """340 341    #: The flags defined for PRIORITY frames.342    defined_flags: list[Flag] = []343 344    #: The type byte defined for PRIORITY frames.345    type = 0x02346 347    stream_association = _STREAM_ASSOC_HAS_STREAM348 349    def _body_repr(self) -> str:350        return f"exclusive={self.exclusive}, depends_on={self.depends_on}, stream_weight={self.stream_weight}"351 352    def serialize_body(self) -> bytes:353        return self.serialize_priority_data()354 355    def parse_body(self, data: memoryview) -> None:356        if len(data) > 5:357            msg = f"PRIORITY must have 5 byte body: actual length {len(data)}."358            raise InvalidFrameError(msg)359 360        self.parse_priority_data(data)361        self.body_len = 5362 363 364class RstStreamFrame(Frame):365    """366    The RST_STREAM frame allows for abnormal termination of a stream. When sent367    by the initiator of a stream, it indicates that they wish to cancel the368    stream or that an error condition has occurred. When sent by the receiver369    of a stream, it indicates that either the receiver is rejecting the stream,370    requesting that the stream be cancelled or that an error condition has371    occurred.372    """373 374    #: The flags defined for RST_STREAM frames.375    defined_flags: list[Flag] = []376 377    #: The type byte defined for RST_STREAM frames.378    type = 0x03379 380    stream_association = _STREAM_ASSOC_HAS_STREAM381 382    def __init__(self, stream_id: int, error_code: int = 0, **kwargs: Any) -> None:383        super().__init__(stream_id, **kwargs)384 385        #: The error code used when resetting the stream.386        self.error_code = error_code387 388    def _body_repr(self) -> str:389        return f"error_code={self.error_code}"390 391    def serialize_body(self) -> bytes:392        return _STRUCT_L.pack(self.error_code)393 394    def parse_body(self, data: memoryview) -> None:395        if len(data) != 4:396            msg = f"RST_STREAM must have 4 byte body: actual length {len(data)}."397            raise InvalidFrameError(msg)398 399        try:400            self.error_code = _STRUCT_L.unpack(data)[0]401        except struct.error as err:  # pragma: no cover402            msg = "Invalid RST_STREAM body"403            raise InvalidFrameError(msg) from err404 405        self.body_len = 4406 407 408class SettingsFrame(Frame):409    """410    The SETTINGS frame conveys configuration parameters that affect how411    endpoints communicate. The parameters are either constraints on peer412    behavior or preferences.413 414    Settings are not negotiated. Settings describe characteristics of the415    sending peer, which are used by the receiving peer. Different values for416    the same setting can be advertised by each peer. For example, a client417    might set a high initial flow control window, whereas a server might set a418    lower value to conserve resources.419    """420 421    #: The flags defined for SETTINGS frames.422    defined_flags = [Flag("ACK", 0x01)]423 424    #: The type byte defined for SETTINGS frames.425    type = 0x04426 427    stream_association = _STREAM_ASSOC_NO_STREAM428 429    # We need to define the known settings, they may as well be class430    # attributes.431    #: The byte that signals the SETTINGS_HEADER_TABLE_SIZE setting.432    HEADER_TABLE_SIZE = 0x01433    #: The byte that signals the SETTINGS_ENABLE_PUSH setting.434    ENABLE_PUSH = 0x02435    #: The byte that signals the SETTINGS_MAX_CONCURRENT_STREAMS setting.436    MAX_CONCURRENT_STREAMS = 0x03437    #: The byte that signals the SETTINGS_INITIAL_WINDOW_SIZE setting.438    INITIAL_WINDOW_SIZE = 0x04439    #: The byte that signals the SETTINGS_MAX_FRAME_SIZE setting.440    MAX_FRAME_SIZE = 0x05441    #: The byte that signals the SETTINGS_MAX_HEADER_LIST_SIZE setting.442    MAX_HEADER_LIST_SIZE = 0x06443    #: The byte that signals SETTINGS_ENABLE_CONNECT_PROTOCOL setting.444    ENABLE_CONNECT_PROTOCOL = 0x08445 446    def __init__(self, stream_id: int = 0, settings: dict[int, int] | None = None, **kwargs: Any) -> None:447        super().__init__(stream_id, **kwargs)448 449        if settings and "ACK" in kwargs.get("flags", ()):450            msg = "Settings must be empty if ACK flag is set."451            raise InvalidDataError(msg)452 453        #: A dictionary of the setting type byte to the value of the setting.454        self.settings: dict[int, int] = settings or {}455 456    def _body_repr(self) -> str:457        return f"settings={self.settings}"458 459    def serialize_body(self) -> bytes:460        return b"".join([_STRUCT_HL.pack(setting & 0xFF, value)461                         for setting, value in self.settings.items()])462 463    def parse_body(self, data: memoryview) -> None:464        if "ACK" in self.flags and len(data) > 0:465            msg = f"SETTINGS ack frame must not have payload: got {len(data)} bytes"466            raise InvalidDataError(msg)467 468        body_len = 0469        for i in range(0, len(data), 6):470            try:471                name, value = _STRUCT_HL.unpack(data[i:i+6])472            except struct.error as err:473                msg = "Invalid SETTINGS body"474                raise InvalidFrameError(msg) from err475 476            self.settings[name] = value477            body_len += 6478 479        self.body_len = body_len480 481 482class PushPromiseFrame(Padding, Frame):483    """484    The PUSH_PROMISE frame is used to notify the peer endpoint in advance of485    streams the sender intends to initiate.486    """487 488    #: The flags defined for PUSH_PROMISE frames.489    defined_flags = [490        Flag("END_HEADERS", 0x04),491        Flag("PADDED", 0x08),492    ]493 494    #: The type byte defined for PUSH_PROMISE frames.495    type = 0x05496 497    stream_association = _STREAM_ASSOC_HAS_STREAM498 499    def __init__(self, stream_id: int, promised_stream_id: int = 0, data: bytes = b"", **kwargs: Any) -> None:500        super().__init__(stream_id, **kwargs)501 502        #: The stream ID that is promised by this frame.503        self.promised_stream_id = promised_stream_id504 505        #: The HPACK-encoded header block for the simulated request on the new506        #: stream.507        self.data = data508 509    def _body_repr(self) -> str:510        return f"promised_stream_id={self.promised_stream_id}, data={_raw_data_repr(self.data)}"511 512    def serialize_body(self) -> bytes:513        padding_data = self.serialize_padding_data()514        padding = b"\0" * self.pad_length515        data = _STRUCT_L.pack(self.promised_stream_id)516        return b"".join([padding_data, data, self.data, padding])517 518    def parse_body(self, data: memoryview) -> None:519        padding_data_length = self.parse_padding_data(data)520 521        try:522            self.promised_stream_id = _STRUCT_L.unpack(523                data[padding_data_length:padding_data_length + 4],524            )[0]525        except struct.error as err:526            msg = "Invalid PUSH_PROMISE body"527            raise InvalidFrameError(msg) from err528 529        self.data = (530            data[padding_data_length + 4:len(data)-self.pad_length].tobytes()531        )532        self.body_len = len(data)533 534        if self.promised_stream_id == 0 or self.promised_stream_id % 2 != 0:535            msg = f"Invalid PUSH_PROMISE promised stream id: {self.promised_stream_id}"536            raise InvalidDataError(msg)537 538        if self.pad_length and self.pad_length >= self.body_len:539            msg = "Padding is too long."540            raise InvalidPaddingError(msg)541 542 543class PingFrame(Frame):544    """545    The PING frame is a mechanism for measuring a minimal round-trip time from546    the sender, as well as determining whether an idle connection is still547    functional. PING frames can be sent from any endpoint.548    """549 550    #: The flags defined for PING frames.551    defined_flags = [Flag("ACK", 0x01)]552 553    #: The type byte defined for PING frames.554    type = 0x06555 556    stream_association = _STREAM_ASSOC_NO_STREAM557 558    def __init__(self, stream_id: int = 0, opaque_data: bytes = b"", **kwargs: Any) -> None:559        super().__init__(stream_id, **kwargs)560 561        #: The opaque data sent in this PING frame, as a bytestring.562        self.opaque_data = opaque_data563 564    def _body_repr(self) -> str:565        return f"opaque_data={self.opaque_data!r}"566 567    def serialize_body(self) -> bytes:568        if len(self.opaque_data) > 8:569            msg = f"PING frame may not have more than 8 bytes of data, got {len(self.opaque_data)}"570            raise InvalidFrameError(msg)571 572        data = self.opaque_data573        data += b"\x00" * (8 - len(self.opaque_data))574        return data575 576    def parse_body(self, data: memoryview) -> None:577        if len(data) != 8:578            msg = f"PING frame must have 8 byte length: got {len(data)}"579            raise InvalidFrameError(msg)580 581        self.opaque_data = data.tobytes()582        self.body_len = 8583 584 585class GoAwayFrame(Frame):586    """587    The GOAWAY frame informs the remote peer to stop creating streams on this588    connection. It can be sent from the client or the server. Once sent, the589    sender will ignore frames sent on new streams for the remainder of the590    connection.591    """592 593    #: The flags defined for GOAWAY frames.594    defined_flags: list[Flag] = []595 596    #: The type byte defined for GOAWAY frames.597    type = 0x07598 599    stream_association = _STREAM_ASSOC_NO_STREAM600 601    def __init__(self,602                 stream_id: int = 0,603                 last_stream_id: int = 0,604                 error_code: int = 0,605                 additional_data: bytes = b"",606                 **kwargs: Any) -> None:607        super().__init__(stream_id, **kwargs)608 609        #: The last stream ID definitely seen by the remote peer.610        self.last_stream_id = last_stream_id611 612        #: The error code for connection teardown.613        self.error_code = error_code614 615        #: Any additional data sent in the GOAWAY.616        self.additional_data = additional_data617 618    def _body_repr(self) -> str:619        return f"last_stream_id={self.last_stream_id}, error_code={self.error_code}, additional_data={self.additional_data!r}"620 621    def serialize_body(self) -> bytes:622        data = _STRUCT_LL.pack(623            self.last_stream_id & 0x7FFFFFFF,624            self.error_code,625        )626        data += self.additional_data627 628        return data629 630    def parse_body(self, data: memoryview) -> None:631        try:632            self.last_stream_id, self.error_code = _STRUCT_LL.unpack(633                data[:8],634            )635        except struct.error as err:636            msg = "Invalid GOAWAY body."637            raise InvalidFrameError(msg) from err638 639        self.body_len = len(data)640 641        if len(data) > 8:642            self.additional_data = data[8:].tobytes()643 644 645class WindowUpdateFrame(Frame):646    """647    The WINDOW_UPDATE frame is used to implement flow control.648 649    Flow control operates at two levels: on each individual stream and on the650    entire connection.651 652    Both types of flow control are hop by hop; that is, only between the two653    endpoints. Intermediaries do not forward WINDOW_UPDATE frames between654    dependent connections. However, throttling of data transfer by any receiver655    can indirectly cause the propagation of flow control information toward the656    original sender.657    """658 659    #: The flags defined for WINDOW_UPDATE frames.660    defined_flags: list[Flag] = []661 662    #: The type byte defined for WINDOW_UPDATE frames.663    type = 0x08664 665    stream_association = _STREAM_ASSOC_EITHER666 667    def __init__(self, stream_id: int, window_increment: int = 0, **kwargs: Any) -> None:668        super().__init__(stream_id, **kwargs)669 670        #: The amount the flow control window is to be incremented.671        self.window_increment = window_increment672 673    def _body_repr(self) -> str:674        return f"window_increment={self.window_increment}"675 676    def serialize_body(self) -> bytes:677        return _STRUCT_L.pack(self.window_increment & 0x7FFFFFFF)678 679    def parse_body(self, data: memoryview) -> None:680        if len(data) > 4:681            msg = f"WINDOW_UPDATE frame must have 4 byte length: got {len(data)}"682            raise InvalidFrameError(msg)683 684        try:685            self.window_increment = _STRUCT_L.unpack(data)[0]686        except struct.error as err:687            msg = "Invalid WINDOW_UPDATE body"688            raise InvalidFrameError(msg) from err689 690        if not 1 <= self.window_increment <= 2**31-1:691            msg = "WINDOW_UPDATE increment must be between 1 to 2^31-1"692            raise InvalidDataError(msg)693 694        self.body_len = 4695 696 697class HeadersFrame(Padding, Priority, Frame):698    """699    The HEADERS frame carries name-value pairs. It is used to open a stream.700    HEADERS frames can be sent on a stream in the "open" or "half closed701    (remote)" states.702 703    The HeadersFrame class is actually basically a data frame in this704    implementation, because of the requirement to control the sizes of frames.705    A header block fragment that doesn't fit in an entire HEADERS frame needs706    to be followed with CONTINUATION frames. From the perspective of the frame707    building code the header block is an opaque data segment.708    """709 710    #: The flags defined for HEADERS frames.711    defined_flags = [712        Flag("END_STREAM", 0x01),713        Flag("END_HEADERS", 0x04),714        Flag("PADDED", 0x08),715        Flag("PRIORITY", 0x20),716    ]717 718    #: The type byte defined for HEADERS frames.719    type = 0x01720 721    stream_association = _STREAM_ASSOC_HAS_STREAM722 723    def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None:724        super().__init__(stream_id, **kwargs)725 726        #: The HPACK-encoded header block.727        self.data = data728 729    def _body_repr(self) -> str:730        return f"exclusive={self.exclusive}, depends_on={self.depends_on}, stream_weight={self.stream_weight}, data={_raw_data_repr(self.data)}"731 732    def serialize_body(self) -> bytes:733        padding_data = self.serialize_padding_data()734        padding = b"\0" * self.pad_length735 736        if "PRIORITY" in self.flags:737            priority_data = self.serialize_priority_data()738        else:739            priority_data = b""740 741        return b"".join([padding_data, priority_data, self.data, padding])742 743    def parse_body(self, data: memoryview) -> None:744        padding_data_length = self.parse_padding_data(data)745        data = data[padding_data_length:]746 747        if "PRIORITY" in self.flags:748            priority_data_length = self.parse_priority_data(data)749        else:750            priority_data_length = 0751 752        self.body_len = len(data)753        self.data = (754            data[priority_data_length:len(data)-self.pad_length].tobytes()755        )756 757        if self.pad_length and self.pad_length >= self.body_len:758            msg = "Padding is too long."759            raise InvalidPaddingError(msg)760 761 762class ContinuationFrame(Frame):763    """764    The CONTINUATION frame is used to continue a sequence of header block765    fragments. Any number of CONTINUATION frames can be sent on an existing766    stream, as long as the preceding frame on the same stream is one of767    HEADERS, PUSH_PROMISE or CONTINUATION without the END_HEADERS flag set.768 769    Much like the HEADERS frame, hyper treats this as an opaque data frame with770    different flags and a different type.771    """772 773    #: The flags defined for CONTINUATION frames.774    defined_flags = [Flag("END_HEADERS", 0x04)]775 776    #: The type byte defined for CONTINUATION frames.777    type = 0x09778 779    stream_association = _STREAM_ASSOC_HAS_STREAM780 781    def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None:782        super().__init__(stream_id, **kwargs)783 784        #: The HPACK-encoded header block.785        self.data = data786 787    def _body_repr(self) -> str:788        return f"data={_raw_data_repr(self.data)}"789 790    def serialize_body(self) -> bytes:791        return self.data792 793    def parse_body(self, data: memoryview) -> None:794        self.data = data.tobytes()795        self.body_len = len(data)796 797 798class AltSvcFrame(Frame):799    """800    The ALTSVC frame is used to advertise alternate services that the current801    host, or a different one, can understand. This frame is standardised as802    part of RFC 7838.803 804    This frame does no work to validate that the ALTSVC field parameter is805    acceptable per the rules of RFC 7838.806 807    .. note:: If the ``stream_id`` of this frame is nonzero, the origin field808              must have zero length. Conversely, if the ``stream_id`` of this809              frame is zero, the origin field must have nonzero length. Put810              another way, a valid ALTSVC frame has ``stream_id != 0`` XOR811              ``len(origin) != 0``.812    """813 814    type = 0x0A815 816    stream_association = _STREAM_ASSOC_EITHER817 818    def __init__(self, stream_id: int, origin: bytes = b"", field: bytes = b"", **kwargs: Any) -> None:819        super().__init__(stream_id, **kwargs)820 821        if not isinstance(origin, bytes):822            msg = "AltSvc origin must be a bytestring."823            raise InvalidDataError(msg)824        if not isinstance(field, bytes):825            msg = "AltSvc field must be a bytestring."826            raise InvalidDataError(msg)827        self.origin = origin828        self.field = field829 830    def _body_repr(self) -> str:831        return f"origin={self.origin!r}, field={self.field!r}"832 833    def serialize_body(self) -> bytes:834        origin_len = _STRUCT_H.pack(len(self.origin))835        return b"".join([origin_len, self.origin, self.field])836 837    def parse_body(self, data: memoryview) -> None:838        try:839            origin_len = _STRUCT_H.unpack(data[0:2])[0]840            self.origin = data[2:2+origin_len].tobytes()841 842            if len(self.origin) != origin_len:843                msg = "Invalid ALTSVC frame body."844                raise InvalidFrameError(msg)845 846            self.field = data[2+origin_len:].tobytes()847        except (struct.error, ValueError) as err:848            msg = "Invalid ALTSVC frame body."849            raise InvalidFrameError(msg) from err850 851        self.body_len = len(data)852 853 854class ExtensionFrame(Frame):855    """856    ExtensionFrame is used to wrap frames which are not natively interpretable857    by hyperframe.858 859    Although certain byte prefixes are ordained by specification to have860    certain contextual meanings, frames with other prefixes are not prohibited,861    and may be used to communicate arbitrary meaning between HTTP/2 peers.862 863    Thus, hyperframe, rather than raising an exception when such a frame is864    encountered, wraps it in a generic frame to be properly acted upon by865    upstream consumers which might have additional context on how to use it.866 867    .. versionadded:: 5.0.0868    """869 870    stream_association = _STREAM_ASSOC_EITHER871 872    def __init__(self, type: int, stream_id: int, flag_byte: int = 0x0, body: bytes = b"", **kwargs: Any) -> None:  # noqa: A002873        super().__init__(stream_id, **kwargs)874        self.type = type875        self.flag_byte = flag_byte876        self.body = body877 878    def _body_repr(self) -> str:879        return f"type={self.type}, flag_byte={self.flag_byte}, body={_raw_data_repr(self.body)}"880 881    def parse_flags(self, flag_byte: int) -> None:  # type: ignore882        """883        For extension frames, we parse the flags by just storing a flag byte.884        """885        self.flag_byte = flag_byte886 887    def parse_body(self, data: memoryview) -> None:888        self.body = data.tobytes()889        self.body_len = len(data)890 891    def serialize(self) -> bytes:892        """893        A broad override of the serialize method that ensures that the data894        comes back out exactly as it came in. This should not be used in most895        user code: it exists only as a helper method if frames need to be896        reconstituted.897        """898        # Build the frame header.899        # First, get the flags.900        flags = self.flag_byte901 902        header = _STRUCT_HBBBL.pack(903            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits904            self.body_len & 0xFF,905            self.type,906            flags,907            self.stream_id & 0x7FFFFFFF,  # Stream ID is 32 bits.908        )909 910        return header + self.body911 912 913def _raw_data_repr(data: bytes | None) -> str:914    if not data:915        return "None"916    r = binascii.hexlify(data).decode("ascii")917    if len(r) > 20:918        r = r[:20] + "..."919    return "<hex:" + r + ">"920 921 922_FRAME_CLASSES: list[type[Frame]] = [923    DataFrame,924    HeadersFrame,925    PriorityFrame,926    RstStreamFrame,927    SettingsFrame,928    PushPromiseFrame,929    PingFrame,930    GoAwayFrame,931    WindowUpdateFrame,932    ContinuationFrame,933    AltSvcFrame,934]935#: FRAMES maps the type byte for each frame to the class used to represent that936#: frame.937FRAMES = {cls.type: cls for cls in _FRAME_CLASSES}938 
codekingpro/portable-devtools · Team Ai