about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/hyperframe
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/hyperframe
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/hyperframe')
-rw-r--r--.venv/lib/python3.12/site-packages/hyperframe/__init__.py6
-rw-r--r--.venv/lib/python3.12/site-packages/hyperframe/exceptions.py61
-rw-r--r--.venv/lib/python3.12/site-packages/hyperframe/flags.py47
-rw-r--r--.venv/lib/python3.12/site-packages/hyperframe/frame.py937
-rw-r--r--.venv/lib/python3.12/site-packages/hyperframe/py.typed0
5 files changed, 1051 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/hyperframe/__init__.py b/.venv/lib/python3.12/site-packages/hyperframe/__init__.py
new file mode 100644
index 00000000..e95b20b9
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/hyperframe/__init__.py
@@ -0,0 +1,6 @@
+"""
+Provides a pure-Python HTTP/2 framing layer.
+"""
+from __future__ import annotations
+
+__version__ = "6.1.0"
diff --git a/.venv/lib/python3.12/site-packages/hyperframe/exceptions.py b/.venv/lib/python3.12/site-packages/hyperframe/exceptions.py
new file mode 100644
index 00000000..7a40f4ab
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/hyperframe/exceptions.py
@@ -0,0 +1,61 @@
+"""
+Exceptions that can be thrown by hyperframe.
+"""
+from __future__ import annotations
+
+
+class HyperframeError(Exception):
+    """
+    The base class for all exceptions for the hyperframe module.
+
+    .. versionadded:: 6.0.0
+    """
+
+
+class UnknownFrameError(HyperframeError):
+    """
+    A frame of unknown type was received.
+
+    .. versionchanged:: 6.0.0
+        Changed base class from `ValueError` to :class:`HyperframeError`
+    """
+
+    def __init__(self, frame_type: int, length: int) -> None:
+        #: The type byte of the unknown frame that was received.
+        self.frame_type = frame_type
+
+        #: The length of the data portion of the unknown frame.
+        self.length = length
+
+    def __str__(self) -> str:
+        return (
+            f"UnknownFrameError: Unknown frame type 0x{self.frame_type:X} received, length {self.length} bytes"
+        )
+
+
+class InvalidPaddingError(HyperframeError):
+    """
+    A frame with invalid padding was received.
+
+    .. versionchanged:: 6.0.0
+        Changed base class from `ValueError` to :class:`HyperframeError`
+    """
+
+
+class InvalidFrameError(HyperframeError):
+    """
+    Parsing a frame failed because the data was not laid out appropriately.
+
+    .. versionadded:: 3.0.2
+
+    .. versionchanged:: 6.0.0
+        Changed base class from `ValueError` to :class:`HyperframeError`
+    """
+
+
+class InvalidDataError(HyperframeError):
+    """
+    Content or data of a frame was is invalid or violates the specification.
+
+    .. versionadded:: 6.0.0
+    """
diff --git a/.venv/lib/python3.12/site-packages/hyperframe/flags.py b/.venv/lib/python3.12/site-packages/hyperframe/flags.py
new file mode 100644
index 00000000..e5f4a22b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/hyperframe/flags.py
@@ -0,0 +1,47 @@
+"""
+Basic Flag and Flags data structures.
+"""
+from __future__ import annotations
+
+from collections.abc import Iterable, Iterator, MutableSet
+from typing import NamedTuple
+
+
+class Flag(NamedTuple):
+    name: str
+    bit: int
+
+
+class Flags(MutableSet):  # type: ignore
+    """
+    A simple MutableSet implementation that will only accept known flags as
+    elements.
+
+    Will behave like a regular set(), except that a ValueError will be thrown
+    when .add()ing unexpected flags.
+    """
+
+    def __init__(self, defined_flags: Iterable[Flag]) -> None:
+        self._valid_flags = {flag.name for flag in defined_flags}
+        self._flags: set[str] = set()
+
+    def __repr__(self) -> str:
+        return repr(sorted(self._flags))
+
+    def __contains__(self, x: object) -> bool:
+        return self._flags.__contains__(x)
+
+    def __iter__(self) -> Iterator[str]:
+        return self._flags.__iter__()
+
+    def __len__(self) -> int:
+        return self._flags.__len__()
+
+    def discard(self, value: str) -> None:
+        return self._flags.discard(value)
+
+    def add(self, value: str) -> None:
+        if value not in self._valid_flags:
+            msg = f"Unexpected flag: {value}. Valid flags are: {self._valid_flags}"
+            raise ValueError(msg)
+        return self._flags.add(value)
diff --git a/.venv/lib/python3.12/site-packages/hyperframe/frame.py b/.venv/lib/python3.12/site-packages/hyperframe/frame.py
new file mode 100644
index 00000000..a67487e0
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/hyperframe/frame.py
@@ -0,0 +1,937 @@
+"""
+Framing logic for HTTP/2.
+
+Provides both classes to represent framed
+data and logic for aiding the connection when it comes to reading from the
+socket.
+"""
+from __future__ import annotations
+
+import binascii
+import struct
+from typing import TYPE_CHECKING, Any
+
+if TYPE_CHECKING:
+    from collections.abc import Iterable  # pragma: no cover
+
+from .exceptions import InvalidDataError, InvalidFrameError, InvalidPaddingError, UnknownFrameError
+from .flags import Flag, Flags
+
+# The maximum initial length of a frame. Some frames have shorter maximum
+# lengths.
+FRAME_MAX_LEN = (2 ** 14)
+
+# The maximum allowed length of a frame.
+FRAME_MAX_ALLOWED_LEN = (2 ** 24) - 1
+
+# Stream association enumerations.
+_STREAM_ASSOC_HAS_STREAM = "has-stream"
+_STREAM_ASSOC_NO_STREAM = "no-stream"
+_STREAM_ASSOC_EITHER = "either"
+
+# Structs for packing and unpacking
+_STRUCT_HBBBL = struct.Struct(">HBBBL")
+_STRUCT_LL = struct.Struct(">LL")
+_STRUCT_HL = struct.Struct(">HL")
+_STRUCT_LB = struct.Struct(">LB")
+_STRUCT_L = struct.Struct(">L")
+_STRUCT_H = struct.Struct(">H")
+_STRUCT_B = struct.Struct(">B")
+
+
+class Frame:
+    """
+    The base class for all HTTP/2 frames.
+    """
+
+    #: The flags defined on this type of frame.
+    defined_flags: list[Flag] = []
+
+    #: The byte used to define the type of the frame.
+    type: int | None = None
+
+    # If 'has-stream', the frame's stream_id must be non-zero. If 'no-stream',
+    # it must be zero. If 'either', it's not checked.
+    stream_association: str | None = None
+
+    def __init__(self, stream_id: int, flags: Iterable[str] = ()) -> None:
+        #: The stream identifier for the stream this frame was received on.
+        #: Set to 0 for frames sent on the connection (stream-id 0).
+        self.stream_id = stream_id
+
+        #: The flags set for this frame.
+        self.flags = Flags(self.defined_flags)
+
+        #: The frame length, excluding the nine-byte header.
+        self.body_len = 0
+
+        for flag in flags:
+            self.flags.add(flag)
+
+        if not self.stream_id and self.stream_association == _STREAM_ASSOC_HAS_STREAM:
+            msg = f"Stream ID must be non-zero for {type(self).__name__}"
+            raise InvalidDataError(msg)
+        if self.stream_id and self.stream_association == _STREAM_ASSOC_NO_STREAM:
+            msg = f"Stream ID must be zero for {type(self).__name__} with stream_id={self.stream_id}"
+            raise InvalidDataError(msg)
+
+    def __repr__(self) -> str:
+        return (
+            f"{type(self).__name__}(stream_id={self.stream_id}, flags={self.flags!r}): {self._body_repr()}"
+        )
+
+    def _body_repr(self) -> str:
+        # More specific implementation may be provided by subclasses of Frame.
+        # This fallback shows the serialized (and truncated) body content.
+        return _raw_data_repr(self.serialize_body())
+
+    @staticmethod
+    def explain(data: memoryview) -> tuple[Frame, int]:
+        """
+        Takes a bytestring and tries to parse a single frame and print it.
+
+        This function is only provided for debugging purposes.
+
+        :param data: A memoryview object containing the raw data of at least
+                     one complete frame (header and body).
+
+        .. versionadded:: 6.0.0
+        """
+        frame, length = Frame.parse_frame_header(data[:9])
+        frame.parse_body(data[9:9 + length])
+        print(frame)  # noqa: T201
+        return frame, length
+
+    @staticmethod
+    def parse_frame_header(header: memoryview, strict: bool = False) -> tuple[Frame, int]:
+        """
+        Takes a 9-byte frame header and returns a tuple of the appropriate
+        Frame object and the length that needs to be read from the socket.
+
+        This populates the flags field, and determines how long the body is.
+
+        :param header: A memoryview object containing the 9-byte frame header
+                       data of a frame. Must not contain more or less.
+
+        :param strict: Whether to raise an exception when encountering a frame
+            not defined by spec and implemented by hyperframe.
+
+        :raises hyperframe.exceptions.UnknownFrameError: If a frame of unknown
+            type is received.
+
+        .. versionchanged:: 5.0.0
+            Added ``strict`` parameter to accommodate :class:`ExtensionFrame`
+        """
+        try:
+            fields = _STRUCT_HBBBL.unpack(header)
+        except struct.error as err:
+            msg = "Invalid frame header"
+            raise InvalidFrameError(msg) from err
+
+        # First 24 bits are frame length.
+        length = (fields[0] << 8) + fields[1]
+        typ_e = fields[2]
+        flags = fields[3]
+        stream_id = fields[4] & 0x7FFFFFFF
+
+        try:
+            frame = FRAMES[typ_e](stream_id)
+        except KeyError as err:
+            if strict:
+                raise UnknownFrameError(typ_e, length) from err
+            frame = ExtensionFrame(type=typ_e, stream_id=stream_id)
+
+        frame.parse_flags(flags)
+        return (frame, length)
+
+    def parse_flags(self, flag_byte: int) -> Flags:
+        for flag, flag_bit in self.defined_flags:
+            if flag_byte & flag_bit:
+                self.flags.add(flag)
+
+        return self.flags
+
+    def serialize(self) -> bytes:
+        """
+        Convert a frame into a bytestring, representing the serialized form of
+        the frame.
+        """
+        body = self.serialize_body()
+        self.body_len = len(body)
+
+        # Build the common frame header.
+        # First, get the flags.
+        flags = 0
+
+        for flag, flag_bit in self.defined_flags:
+            if flag in self.flags:
+                flags |= flag_bit
+
+        header = _STRUCT_HBBBL.pack(
+            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits
+            self.body_len & 0xFF,
+            self.type,
+            flags,
+            self.stream_id & 0x7FFFFFFF,  # Stream ID is 32 bits.
+        )
+
+        return header + body
+
+    def serialize_body(self) -> bytes:
+        raise NotImplementedError
+
+    def parse_body(self, data: memoryview) -> None:
+        """
+        Given the body of a frame, parses it into frame data. This populates
+        the non-header parts of the frame: that is, it does not populate the
+        stream ID or flags.
+
+        :param data: A memoryview object containing the body data of the frame.
+                     Must not contain *more* data than the length returned by
+                     :meth:`parse_frame_header
+                     <hyperframe.frame.Frame.parse_frame_header>`.
+        """
+        raise NotImplementedError
+
+
+class Padding:
+    """
+    Mixin for frames that contain padding. Defines extra fields that can be
+    used and set by frames that can be padded.
+    """
+
+    def __init__(self, stream_id: int, pad_length: int = 0, **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)  # type: ignore
+
+        #: The length of the padding to use.
+        self.pad_length = pad_length
+
+    def serialize_padding_data(self) -> bytes:
+        if "PADDED" in self.flags:  # type: ignore
+            return _STRUCT_B.pack(self.pad_length)
+        return b""
+
+    def parse_padding_data(self, data: memoryview) -> int:
+        if "PADDED" in self.flags:  # type: ignore
+            try:
+                self.pad_length = struct.unpack("!B", data[:1])[0]
+            except struct.error as err:
+                msg = "Invalid Padding data"
+                raise InvalidFrameError(msg) from err
+            return 1
+        return 0
+
+    #: .. deprecated:: 5.2.1
+    #:    Use self.pad_length instead.
+    @property
+    def total_padding(self) -> int:  # pragma: no cover
+        import warnings
+        warnings.warn(
+            "total_padding contains the same information as pad_length.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return self.pad_length
+
+
+class Priority:
+    """
+    Mixin for frames that contain priority data. Defines extra fields that can
+    be used and set by frames that contain priority data.
+    """
+
+    def __init__(self,
+                 stream_id: int,
+                 depends_on: int = 0x0,
+                 stream_weight: int = 0x0,
+                 exclusive: bool = False,
+                 **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)  # type: ignore
+
+        #: The stream ID of the stream on which this stream depends.
+        self.depends_on = depends_on
+
+        #: The weight of the stream. This is an integer between 0 and 256.
+        self.stream_weight = stream_weight
+
+        #: Whether the exclusive bit was set.
+        self.exclusive = exclusive
+
+    def serialize_priority_data(self) -> bytes:
+        return _STRUCT_LB.pack(
+            self.depends_on + (0x80000000 if self.exclusive else 0),
+            self.stream_weight,
+        )
+
+    def parse_priority_data(self, data: memoryview) -> int:
+        try:
+            self.depends_on, self.stream_weight = _STRUCT_LB.unpack(data[:5])
+        except struct.error as err:
+            msg = "Invalid Priority data"
+            raise InvalidFrameError(msg) from err
+
+        self.exclusive = bool(self.depends_on >> 31)
+        self.depends_on &= 0x7FFFFFFF
+        return 5
+
+
+class DataFrame(Padding, Frame):
+    """
+    DATA frames convey arbitrary, variable-length sequences of octets
+    associated with a stream. One or more DATA frames are used, for instance,
+    to carry HTTP request or response payloads.
+    """
+
+    #: The flags defined for DATA frames.
+    defined_flags = [
+        Flag("END_STREAM", 0x01),
+        Flag("PADDED", 0x08),
+    ]
+
+    #: The type byte for data frames.
+    type = 0x0
+
+    stream_association = _STREAM_ASSOC_HAS_STREAM
+
+    def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The data contained on this frame.
+        self.data = data
+
+    def serialize_body(self) -> bytes:
+        padding_data = self.serialize_padding_data()
+        padding = b"\0" * self.pad_length
+        if isinstance(self.data, memoryview):
+            self.data = self.data.tobytes()
+        return b"".join([padding_data, self.data, padding])
+
+    def parse_body(self, data: memoryview) -> None:
+        padding_data_length = self.parse_padding_data(data)
+        self.data = (
+            data[padding_data_length:len(data)-self.pad_length].tobytes()
+        )
+        self.body_len = len(data)
+
+        if self.pad_length and self.pad_length >= self.body_len:
+            msg = "Padding is too long."
+            raise InvalidPaddingError(msg)
+
+    @property
+    def flow_controlled_length(self) -> int:
+        """
+        The length of the frame that needs to be accounted for when considering
+        flow control.
+        """
+        padding_len = 0
+        if "PADDED" in self.flags:
+            # Account for extra 1-byte padding length field, which is still
+            # present if possibly zero-valued.
+            padding_len = self.pad_length + 1
+        return len(self.data) + padding_len
+
+
+class PriorityFrame(Priority, Frame):
+    """
+    The PRIORITY frame specifies the sender-advised priority of a stream. It
+    can be sent at any time for an existing stream. This enables
+    reprioritisation of existing streams.
+    """
+
+    #: The flags defined for PRIORITY frames.
+    defined_flags: list[Flag] = []
+
+    #: The type byte defined for PRIORITY frames.
+    type = 0x02
+
+    stream_association = _STREAM_ASSOC_HAS_STREAM
+
+    def _body_repr(self) -> str:
+        return f"exclusive={self.exclusive}, depends_on={self.depends_on}, stream_weight={self.stream_weight}"
+
+    def serialize_body(self) -> bytes:
+        return self.serialize_priority_data()
+
+    def parse_body(self, data: memoryview) -> None:
+        if len(data) > 5:
+            msg = f"PRIORITY must have 5 byte body: actual length {len(data)}."
+            raise InvalidFrameError(msg)
+
+        self.parse_priority_data(data)
+        self.body_len = 5
+
+
+class RstStreamFrame(Frame):
+    """
+    The RST_STREAM frame allows for abnormal termination of a stream. When sent
+    by the initiator of a stream, it indicates that they wish to cancel the
+    stream or that an error condition has occurred. When sent by the receiver
+    of a stream, it indicates that either the receiver is rejecting the stream,
+    requesting that the stream be cancelled or that an error condition has
+    occurred.
+    """
+
+    #: The flags defined for RST_STREAM frames.
+    defined_flags: list[Flag] = []
+
+    #: The type byte defined for RST_STREAM frames.
+    type = 0x03
+
+    stream_association = _STREAM_ASSOC_HAS_STREAM
+
+    def __init__(self, stream_id: int, error_code: int = 0, **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The error code used when resetting the stream.
+        self.error_code = error_code
+
+    def _body_repr(self) -> str:
+        return f"error_code={self.error_code}"
+
+    def serialize_body(self) -> bytes:
+        return _STRUCT_L.pack(self.error_code)
+
+    def parse_body(self, data: memoryview) -> None:
+        if len(data) != 4:
+            msg = f"RST_STREAM must have 4 byte body: actual length {len(data)}."
+            raise InvalidFrameError(msg)
+
+        try:
+            self.error_code = _STRUCT_L.unpack(data)[0]
+        except struct.error as err:  # pragma: no cover
+            msg = "Invalid RST_STREAM body"
+            raise InvalidFrameError(msg) from err
+
+        self.body_len = 4
+
+
+class SettingsFrame(Frame):
+    """
+    The SETTINGS frame conveys configuration parameters that affect how
+    endpoints communicate. The parameters are either constraints on peer
+    behavior or preferences.
+
+    Settings are not negotiated. Settings describe characteristics of the
+    sending peer, which are used by the receiving peer. Different values for
+    the same setting can be advertised by each peer. For example, a client
+    might set a high initial flow control window, whereas a server might set a
+    lower value to conserve resources.
+    """
+
+    #: The flags defined for SETTINGS frames.
+    defined_flags = [Flag("ACK", 0x01)]
+
+    #: The type byte defined for SETTINGS frames.
+    type = 0x04
+
+    stream_association = _STREAM_ASSOC_NO_STREAM
+
+    # We need to define the known settings, they may as well be class
+    # attributes.
+    #: The byte that signals the SETTINGS_HEADER_TABLE_SIZE setting.
+    HEADER_TABLE_SIZE = 0x01
+    #: The byte that signals the SETTINGS_ENABLE_PUSH setting.
+    ENABLE_PUSH = 0x02
+    #: The byte that signals the SETTINGS_MAX_CONCURRENT_STREAMS setting.
+    MAX_CONCURRENT_STREAMS = 0x03
+    #: The byte that signals the SETTINGS_INITIAL_WINDOW_SIZE setting.
+    INITIAL_WINDOW_SIZE = 0x04
+    #: The byte that signals the SETTINGS_MAX_FRAME_SIZE setting.
+    MAX_FRAME_SIZE = 0x05
+    #: The byte that signals the SETTINGS_MAX_HEADER_LIST_SIZE setting.
+    MAX_HEADER_LIST_SIZE = 0x06
+    #: The byte that signals SETTINGS_ENABLE_CONNECT_PROTOCOL setting.
+    ENABLE_CONNECT_PROTOCOL = 0x08
+
+    def __init__(self, stream_id: int = 0, settings: dict[int, int] | None = None, **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        if settings and "ACK" in kwargs.get("flags", ()):
+            msg = "Settings must be empty if ACK flag is set."
+            raise InvalidDataError(msg)
+
+        #: A dictionary of the setting type byte to the value of the setting.
+        self.settings: dict[int, int] = settings or {}
+
+    def _body_repr(self) -> str:
+        return f"settings={self.settings}"
+
+    def serialize_body(self) -> bytes:
+        return b"".join([_STRUCT_HL.pack(setting & 0xFF, value)
+                         for setting, value in self.settings.items()])
+
+    def parse_body(self, data: memoryview) -> None:
+        if "ACK" in self.flags and len(data) > 0:
+            msg = f"SETTINGS ack frame must not have payload: got {len(data)} bytes"
+            raise InvalidDataError(msg)
+
+        body_len = 0
+        for i in range(0, len(data), 6):
+            try:
+                name, value = _STRUCT_HL.unpack(data[i:i+6])
+            except struct.error as err:
+                msg = "Invalid SETTINGS body"
+                raise InvalidFrameError(msg) from err
+
+            self.settings[name] = value
+            body_len += 6
+
+        self.body_len = body_len
+
+
+class PushPromiseFrame(Padding, Frame):
+    """
+    The PUSH_PROMISE frame is used to notify the peer endpoint in advance of
+    streams the sender intends to initiate.
+    """
+
+    #: The flags defined for PUSH_PROMISE frames.
+    defined_flags = [
+        Flag("END_HEADERS", 0x04),
+        Flag("PADDED", 0x08),
+    ]
+
+    #: The type byte defined for PUSH_PROMISE frames.
+    type = 0x05
+
+    stream_association = _STREAM_ASSOC_HAS_STREAM
+
+    def __init__(self, stream_id: int, promised_stream_id: int = 0, data: bytes = b"", **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The stream ID that is promised by this frame.
+        self.promised_stream_id = promised_stream_id
+
+        #: The HPACK-encoded header block for the simulated request on the new
+        #: stream.
+        self.data = data
+
+    def _body_repr(self) -> str:
+        return f"promised_stream_id={self.promised_stream_id}, data={_raw_data_repr(self.data)}"
+
+    def serialize_body(self) -> bytes:
+        padding_data = self.serialize_padding_data()
+        padding = b"\0" * self.pad_length
+        data = _STRUCT_L.pack(self.promised_stream_id)
+        return b"".join([padding_data, data, self.data, padding])
+
+    def parse_body(self, data: memoryview) -> None:
+        padding_data_length = self.parse_padding_data(data)
+
+        try:
+            self.promised_stream_id = _STRUCT_L.unpack(
+                data[padding_data_length:padding_data_length + 4],
+            )[0]
+        except struct.error as err:
+            msg = "Invalid PUSH_PROMISE body"
+            raise InvalidFrameError(msg) from err
+
+        self.data = (
+            data[padding_data_length + 4:len(data)-self.pad_length].tobytes()
+        )
+        self.body_len = len(data)
+
+        if self.promised_stream_id == 0 or self.promised_stream_id % 2 != 0:
+            msg = f"Invalid PUSH_PROMISE promised stream id: {self.promised_stream_id}"
+            raise InvalidDataError(msg)
+
+        if self.pad_length and self.pad_length >= self.body_len:
+            msg = "Padding is too long."
+            raise InvalidPaddingError(msg)
+
+
+class PingFrame(Frame):
+    """
+    The PING frame is a mechanism for measuring a minimal round-trip time from
+    the sender, as well as determining whether an idle connection is still
+    functional. PING frames can be sent from any endpoint.
+    """
+
+    #: The flags defined for PING frames.
+    defined_flags = [Flag("ACK", 0x01)]
+
+    #: The type byte defined for PING frames.
+    type = 0x06
+
+    stream_association = _STREAM_ASSOC_NO_STREAM
+
+    def __init__(self, stream_id: int = 0, opaque_data: bytes = b"", **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The opaque data sent in this PING frame, as a bytestring.
+        self.opaque_data = opaque_data
+
+    def _body_repr(self) -> str:
+        return f"opaque_data={self.opaque_data!r}"
+
+    def serialize_body(self) -> bytes:
+        if len(self.opaque_data) > 8:
+            msg = f"PING frame may not have more than 8 bytes of data, got {len(self.opaque_data)}"
+            raise InvalidFrameError(msg)
+
+        data = self.opaque_data
+        data += b"\x00" * (8 - len(self.opaque_data))
+        return data
+
+    def parse_body(self, data: memoryview) -> None:
+        if len(data) != 8:
+            msg = f"PING frame must have 8 byte length: got {len(data)}"
+            raise InvalidFrameError(msg)
+
+        self.opaque_data = data.tobytes()
+        self.body_len = 8
+
+
+class GoAwayFrame(Frame):
+    """
+    The GOAWAY frame informs the remote peer to stop creating streams on this
+    connection. It can be sent from the client or the server. Once sent, the
+    sender will ignore frames sent on new streams for the remainder of the
+    connection.
+    """
+
+    #: The flags defined for GOAWAY frames.
+    defined_flags: list[Flag] = []
+
+    #: The type byte defined for GOAWAY frames.
+    type = 0x07
+
+    stream_association = _STREAM_ASSOC_NO_STREAM
+
+    def __init__(self,
+                 stream_id: int = 0,
+                 last_stream_id: int = 0,
+                 error_code: int = 0,
+                 additional_data: bytes = b"",
+                 **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The last stream ID definitely seen by the remote peer.
+        self.last_stream_id = last_stream_id
+
+        #: The error code for connection teardown.
+        self.error_code = error_code
+
+        #: Any additional data sent in the GOAWAY.
+        self.additional_data = additional_data
+
+    def _body_repr(self) -> str:
+        return f"last_stream_id={self.last_stream_id}, error_code={self.error_code}, additional_data={self.additional_data!r}"
+
+    def serialize_body(self) -> bytes:
+        data = _STRUCT_LL.pack(
+            self.last_stream_id & 0x7FFFFFFF,
+            self.error_code,
+        )
+        data += self.additional_data
+
+        return data
+
+    def parse_body(self, data: memoryview) -> None:
+        try:
+            self.last_stream_id, self.error_code = _STRUCT_LL.unpack(
+                data[:8],
+            )
+        except struct.error as err:
+            msg = "Invalid GOAWAY body."
+            raise InvalidFrameError(msg) from err
+
+        self.body_len = len(data)
+
+        if len(data) > 8:
+            self.additional_data = data[8:].tobytes()
+
+
+class WindowUpdateFrame(Frame):
+    """
+    The WINDOW_UPDATE frame is used to implement flow control.
+
+    Flow control operates at two levels: on each individual stream and on the
+    entire connection.
+
+    Both types of flow control are hop by hop; that is, only between the two
+    endpoints. Intermediaries do not forward WINDOW_UPDATE frames between
+    dependent connections. However, throttling of data transfer by any receiver
+    can indirectly cause the propagation of flow control information toward the
+    original sender.
+    """
+
+    #: The flags defined for WINDOW_UPDATE frames.
+    defined_flags: list[Flag] = []
+
+    #: The type byte defined for WINDOW_UPDATE frames.
+    type = 0x08
+
+    stream_association = _STREAM_ASSOC_EITHER
+
+    def __init__(self, stream_id: int, window_increment: int = 0, **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The amount the flow control window is to be incremented.
+        self.window_increment = window_increment
+
+    def _body_repr(self) -> str:
+        return f"window_increment={self.window_increment}"
+
+    def serialize_body(self) -> bytes:
+        return _STRUCT_L.pack(self.window_increment & 0x7FFFFFFF)
+
+    def parse_body(self, data: memoryview) -> None:
+        if len(data) > 4:
+            msg = f"WINDOW_UPDATE frame must have 4 byte length: got {len(data)}"
+            raise InvalidFrameError(msg)
+
+        try:
+            self.window_increment = _STRUCT_L.unpack(data)[0]
+        except struct.error as err:
+            msg = "Invalid WINDOW_UPDATE body"
+            raise InvalidFrameError(msg) from err
+
+        if not 1 <= self.window_increment <= 2**31-1:
+            msg = "WINDOW_UPDATE increment must be between 1 to 2^31-1"
+            raise InvalidDataError(msg)
+
+        self.body_len = 4
+
+
+class HeadersFrame(Padding, Priority, Frame):
+    """
+    The HEADERS frame carries name-value pairs. It is used to open a stream.
+    HEADERS frames can be sent on a stream in the "open" or "half closed
+    (remote)" states.
+
+    The HeadersFrame class is actually basically a data frame in this
+    implementation, because of the requirement to control the sizes of frames.
+    A header block fragment that doesn't fit in an entire HEADERS frame needs
+    to be followed with CONTINUATION frames. From the perspective of the frame
+    building code the header block is an opaque data segment.
+    """
+
+    #: The flags defined for HEADERS frames.
+    defined_flags = [
+        Flag("END_STREAM", 0x01),
+        Flag("END_HEADERS", 0x04),
+        Flag("PADDED", 0x08),
+        Flag("PRIORITY", 0x20),
+    ]
+
+    #: The type byte defined for HEADERS frames.
+    type = 0x01
+
+    stream_association = _STREAM_ASSOC_HAS_STREAM
+
+    def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The HPACK-encoded header block.
+        self.data = data
+
+    def _body_repr(self) -> str:
+        return f"exclusive={self.exclusive}, depends_on={self.depends_on}, stream_weight={self.stream_weight}, data={_raw_data_repr(self.data)}"
+
+    def serialize_body(self) -> bytes:
+        padding_data = self.serialize_padding_data()
+        padding = b"\0" * self.pad_length
+
+        if "PRIORITY" in self.flags:
+            priority_data = self.serialize_priority_data()
+        else:
+            priority_data = b""
+
+        return b"".join([padding_data, priority_data, self.data, padding])
+
+    def parse_body(self, data: memoryview) -> None:
+        padding_data_length = self.parse_padding_data(data)
+        data = data[padding_data_length:]
+
+        if "PRIORITY" in self.flags:
+            priority_data_length = self.parse_priority_data(data)
+        else:
+            priority_data_length = 0
+
+        self.body_len = len(data)
+        self.data = (
+            data[priority_data_length:len(data)-self.pad_length].tobytes()
+        )
+
+        if self.pad_length and self.pad_length >= self.body_len:
+            msg = "Padding is too long."
+            raise InvalidPaddingError(msg)
+
+
+class ContinuationFrame(Frame):
+    """
+    The CONTINUATION frame is used to continue a sequence of header block
+    fragments. Any number of CONTINUATION frames can be sent on an existing
+    stream, as long as the preceding frame on the same stream is one of
+    HEADERS, PUSH_PROMISE or CONTINUATION without the END_HEADERS flag set.
+
+    Much like the HEADERS frame, hyper treats this as an opaque data frame with
+    different flags and a different type.
+    """
+
+    #: The flags defined for CONTINUATION frames.
+    defined_flags = [Flag("END_HEADERS", 0x04)]
+
+    #: The type byte defined for CONTINUATION frames.
+    type = 0x09
+
+    stream_association = _STREAM_ASSOC_HAS_STREAM
+
+    def __init__(self, stream_id: int, data: bytes = b"", **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        #: The HPACK-encoded header block.
+        self.data = data
+
+    def _body_repr(self) -> str:
+        return f"data={_raw_data_repr(self.data)}"
+
+    def serialize_body(self) -> bytes:
+        return self.data
+
+    def parse_body(self, data: memoryview) -> None:
+        self.data = data.tobytes()
+        self.body_len = len(data)
+
+
+class AltSvcFrame(Frame):
+    """
+    The ALTSVC frame is used to advertise alternate services that the current
+    host, or a different one, can understand. This frame is standardised as
+    part of RFC 7838.
+
+    This frame does no work to validate that the ALTSVC field parameter is
+    acceptable per the rules of RFC 7838.
+
+    .. note:: If the ``stream_id`` of this frame is nonzero, the origin field
+              must have zero length. Conversely, if the ``stream_id`` of this
+              frame is zero, the origin field must have nonzero length. Put
+              another way, a valid ALTSVC frame has ``stream_id != 0`` XOR
+              ``len(origin) != 0``.
+    """
+
+    type = 0x0A
+
+    stream_association = _STREAM_ASSOC_EITHER
+
+    def __init__(self, stream_id: int, origin: bytes = b"", field: bytes = b"", **kwargs: Any) -> None:
+        super().__init__(stream_id, **kwargs)
+
+        if not isinstance(origin, bytes):
+            msg = "AltSvc origin must be a bytestring."
+            raise InvalidDataError(msg)
+        if not isinstance(field, bytes):
+            msg = "AltSvc field must be a bytestring."
+            raise InvalidDataError(msg)
+        self.origin = origin
+        self.field = field
+
+    def _body_repr(self) -> str:
+        return f"origin={self.origin!r}, field={self.field!r}"
+
+    def serialize_body(self) -> bytes:
+        origin_len = _STRUCT_H.pack(len(self.origin))
+        return b"".join([origin_len, self.origin, self.field])
+
+    def parse_body(self, data: memoryview) -> None:
+        try:
+            origin_len = _STRUCT_H.unpack(data[0:2])[0]
+            self.origin = data[2:2+origin_len].tobytes()
+
+            if len(self.origin) != origin_len:
+                msg = "Invalid ALTSVC frame body."
+                raise InvalidFrameError(msg)
+
+            self.field = data[2+origin_len:].tobytes()
+        except (struct.error, ValueError) as err:
+            msg = "Invalid ALTSVC frame body."
+            raise InvalidFrameError(msg) from err
+
+        self.body_len = len(data)
+
+
+class ExtensionFrame(Frame):
+    """
+    ExtensionFrame is used to wrap frames which are not natively interpretable
+    by hyperframe.
+
+    Although certain byte prefixes are ordained by specification to have
+    certain contextual meanings, frames with other prefixes are not prohibited,
+    and may be used to communicate arbitrary meaning between HTTP/2 peers.
+
+    Thus, hyperframe, rather than raising an exception when such a frame is
+    encountered, wraps it in a generic frame to be properly acted upon by
+    upstream consumers which might have additional context on how to use it.
+
+    .. versionadded:: 5.0.0
+    """
+
+    stream_association = _STREAM_ASSOC_EITHER
+
+    def __init__(self, type: int, stream_id: int, flag_byte: int = 0x0, body: bytes = b"", **kwargs: Any) -> None:  # noqa: A002
+        super().__init__(stream_id, **kwargs)
+        self.type = type
+        self.flag_byte = flag_byte
+        self.body = body
+
+    def _body_repr(self) -> str:
+        return f"type={self.type}, flag_byte={self.flag_byte}, body={_raw_data_repr(self.body)}"
+
+    def parse_flags(self, flag_byte: int) -> None:  # type: ignore
+        """
+        For extension frames, we parse the flags by just storing a flag byte.
+        """
+        self.flag_byte = flag_byte
+
+    def parse_body(self, data: memoryview) -> None:
+        self.body = data.tobytes()
+        self.body_len = len(data)
+
+    def serialize(self) -> bytes:
+        """
+        A broad override of the serialize method that ensures that the data
+        comes back out exactly as it came in. This should not be used in most
+        user code: it exists only as a helper method if frames need to be
+        reconstituted.
+        """
+        # Build the frame header.
+        # First, get the flags.
+        flags = self.flag_byte
+
+        header = _STRUCT_HBBBL.pack(
+            (self.body_len >> 8) & 0xFFFF,  # Length spread over top 24 bits
+            self.body_len & 0xFF,
+            self.type,
+            flags,
+            self.stream_id & 0x7FFFFFFF,  # Stream ID is 32 bits.
+        )
+
+        return header + self.body
+
+
+def _raw_data_repr(data: bytes | None) -> str:
+    if not data:
+        return "None"
+    r = binascii.hexlify(data).decode("ascii")
+    if len(r) > 20:
+        r = r[:20] + "..."
+    return "<hex:" + r + ">"
+
+
+_FRAME_CLASSES: list[type[Frame]] = [
+    DataFrame,
+    HeadersFrame,
+    PriorityFrame,
+    RstStreamFrame,
+    SettingsFrame,
+    PushPromiseFrame,
+    PingFrame,
+    GoAwayFrame,
+    WindowUpdateFrame,
+    ContinuationFrame,
+    AltSvcFrame,
+]
+#: FRAMES maps the type byte for each frame to the class used to represent that
+#: frame.
+FRAMES = {cls.type: cls for cls in _FRAME_CLASSES}
diff --git a/.venv/lib/python3.12/site-packages/hyperframe/py.typed b/.venv/lib/python3.12/site-packages/hyperframe/py.typed
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/hyperframe/py.typed