Pure-Python HTTP/2 framing library providing comprehensive frame type support for creating, serializing, and parsing HTTP/2 frames.
—
Core functionality for parsing HTTP/2 frames from binary data and creating new frames with proper validation and serialization support. This forms the foundation of all HTTP/2 frame handling in hyperframe.
The base Frame class provides common functionality for all HTTP/2 frame types including stream ID management, flag handling, and serialization/parsing infrastructure.
class Frame:
def __init__(self, stream_id: int, flags: Iterable[str] = ()) -> None:
"""
Initialize a frame with stream ID and optional flags.
Parameters:
- stream_id (int): Stream identifier (0 for connection-level frames)
- flags (Iterable[str]): Initial flags to set on the frame
Raises:
- InvalidDataError: If stream_id validation fails based on stream_association
"""
@property
def stream_id(self) -> int: ...
@property
def flags(self) -> Flags: ...
@property
def body_len(self) -> int: ...
def serialize(self) -> bytes:
"""
Convert frame into binary representation.
Returns:
bytes: Complete frame including 9-byte header and body
"""
def serialize_body(self) -> bytes:
"""
Serialize frame body data. Must be implemented by subclasses.
Returns:
bytes: Frame body data
Raises:
NotImplementedError: If called on base Frame class
"""
def parse_body(self, data: memoryview) -> None:
"""
Parse frame body from binary data. Must be implemented by subclasses.
Parameters:
- data (memoryview): Body data to parse
Raises:
NotImplementedError: If called on base Frame class
"""
def parse_flags(self, flag_byte: int) -> Flags:
"""
Parse flag byte and set appropriate flags.
Parameters:
- flag_byte (int): 8-bit flag value from frame header
Returns:
Flags: Updated flags object
"""Static methods for parsing frame headers and handling frame discovery from binary streams.
@staticmethod
def parse_frame_header(header: memoryview, strict: bool = False) -> tuple[Frame, int]:
"""
Parse 9-byte frame header and return appropriate Frame object.
Parameters:
- header (memoryview): Exactly 9 bytes of frame header data
- strict (bool): If True, raise exception for unknown frame types
Returns:
tuple[Frame, int]: Frame object and body length to read
Raises:
- InvalidFrameError: If header data is malformed
- UnknownFrameError: If frame type unknown and strict=True
"""
@staticmethod
def explain(data: memoryview) -> tuple[Frame, int]:
"""
Debug helper that parses and prints a complete frame.
Parameters:
- data (memoryview): Complete frame data (header + body)
Returns:
tuple[Frame, int]: Parsed frame and body length
"""Base functionality that can be inherited by frame types supporting padding or priority information.
class Padding:
def __init__(self, stream_id: int, pad_length: int = 0, **kwargs) -> None:
"""
Initialize padding support.
Parameters:
- stream_id (int): Stream identifier
- pad_length (int): Number of padding bytes to add
- **kwargs: Additional arguments passed to parent class
"""
@property
def pad_length(self) -> int: ...
def serialize_padding_data(self) -> bytes:
"""
Serialize padding length field if PADDED flag is set.
Returns:
bytes: Padding length byte or empty if not padded
"""
def parse_padding_data(self, data: memoryview) -> int:
"""
Parse padding length from frame data if PADDED flag is set.
Parameters:
- data (memoryview): Frame body data starting with padding info
Returns:
int: Number of bytes consumed (1 if padded, 0 if not)
Raises:
- InvalidFrameError: If padding data is malformed
"""
class Priority:
def __init__(self, stream_id: int, depends_on: int = 0, stream_weight: int = 0,
exclusive: bool = False, **kwargs) -> None:
"""
Initialize priority support.
Parameters:
- stream_id (int): Stream identifier
- depends_on (int): Stream ID this stream depends on
- stream_weight (int): Stream weight (0-255)
- exclusive (bool): Whether this stream has exclusive dependency
- **kwargs: Additional arguments passed to parent class
"""
@property
def depends_on(self) -> int: ...
@property
def stream_weight(self) -> int: ...
@property
def exclusive(self) -> bool: ...
def serialize_priority_data(self) -> bytes:
"""
Serialize 5-byte priority information.
Returns:
bytes: Priority data (4 bytes stream dependency + 1 byte weight)
"""
def parse_priority_data(self, data: memoryview) -> int:
"""
Parse priority information from frame data.
Parameters:
- data (memoryview): Frame body data starting with priority info
Returns:
int: Number of bytes consumed (always 5)
Raises:
- InvalidFrameError: If priority data is malformed
"""from hyperframe.frame import Frame
# Parse frame from binary data
frame_data = b'\\x00\\x00\\x05\\x00\\x00\\x00\\x00\\x00\\x01Hello'
frame, body_length = Frame.parse_frame_header(frame_data[:9])
frame.parse_body(memoryview(frame_data[9:9 + body_length]))
print(f"Frame type: {frame.type}")
print(f"Stream ID: {frame.stream_id}")
print(f"Body length: {body_length}")from hyperframe.frame import DataFrame
# Create DATA frame with padding
frame = DataFrame(
stream_id=1,
data=b"Hello, HTTP/2!",
pad_length=8,
flags=["END_STREAM", "PADDED"]
)
# Serialize to binary
binary_data = frame.serialize()
print(f"Serialized frame: {len(binary_data)} bytes")from hyperframe.frame import Frame, ExtensionFrame
from hyperframe.exceptions import UnknownFrameError
# Parse with strict=False to handle unknown frames
frame_data = b'\\x00\\x00\\x05\\xFF\\x00\\x00\\x00\\x00\\x01Hello' # Type 0xFF
frame, length = Frame.parse_frame_header(frame_data[:9], strict=False)
if isinstance(frame, ExtensionFrame):
print(f"Unknown frame type: 0x{frame.type:02X}")
frame.parse_body(memoryview(frame_data[9:9 + length]))
print(f"Frame body: {frame.body}")
# Parse with strict=True raises exception
try:
Frame.parse_frame_header(frame_data[:9], strict=True)
except UnknownFrameError as e:
print(f"Unknown frame: type=0x{e.frame_type:02X}, length={e.length}")Install with Tessl CLI
npx tessl i tessl/pypi-hyperframe