Skip to content

blackbull.protocol.frame_types

blackbull.protocol.frame_types

HTTP/2 frame type definitions (RFC 9113 §6; supersedes RFC 7540 §6).

Frame-type classes, enums, and the FrameBase registry. HPACK compression/decompression is delegated to the hpack library.

FrameFactory lives in frame.py; this module is kept free of factory / parsing logic so the type hierarchy can be imported without pulling in the codec.

Continuation

Bases: FrameBase

A CONTINUATION frame — the rest of a field block (RFC 9113 §6.10).

Its payload is a fragment, never a block of its own: append it to the HEADERS or PUSH_PROMISE frame that opened the block and decode there, so the connection-wide HPACK table sees every insertion in order. No frame for another stream may arrive in between, and end_headers marks the last fragment.

Data

Bases: FrameBase

A DATA frame — one chunk of a message body (RFC 9113 §6.1).

payload is the body octets with any padding already removed, so its length is the application's and length is the wire's. Padding that would not fit the frame raises FrameFormatError carrying PROTOCOL_ERROR.

DataFrameFlags

Bases: FrameFlags

Flags a DATA frame may carry (RFC 9113 §6.1).

ErrorCodes

Bases: IntEnum

HTTP/2 error codes (RFC 9113 §7), carried by RST_STREAM and GOAWAY.

The set is open: §7 requires an unknown code to be accepted without special behaviour, so a code read off the wire is not guaranteed to be a member here — RstStream keeps a plain int when the peer sent one this enum does not name.

FrameBase

Common frame header and the subclass registry (RFC 9113 §4.1).

Holds the four fields every frame header carries — length, type_, flags, stream_id — and turns them back into nine octets in save(), which a subclass extends with its own payload. Setting FRAME_TYPE on a subclass registers it for that type; a second class claiming the same type is a ValueError at import.

FrameFlags

Bases: IntEnum

Common annotation base for frame flag enums.

FrameFormatError

Bases: ValueError

Malformed frame with its RFC-defined error code.

FrameTypes

Bases: bytes, Enum

Registered HTTP/2 frame types (RFC 9113 §11.2).

Each member's value is the single type octet exactly as it appears in the frame header, so a member compares equal to the byte read off the wire and not to the integer it encodes. PRIORITY_UPDATE is RFC 9218 §7.1.

GoAway

Bases: FrameBase

RFC 9113 §6.8 — GOAWAY frame.

GOAWAY is connection-level, so the frame header's stream identifier is always 0. last_stream_id is a separate field carried in the first 4 bytes of the payload, followed by the 4-byte error code; a GOAWAY announcing stream 3 still rides on stream 0. Held by tests/unit/test_frame.py::test_goaway_save_includes_last_stream_id_and_error_code.

HeaderFrameFlags

Bases: FrameFlags

Flags a HEADERS frame may carry (RFC 9113 §6.2).

PRIORITY announces the exclusive/dependency/weight fields ahead of the field block; the scheme they describe is deprecated by §5.3, but the five octets still have to be skipped to reach the block.

Headers

Bases: FrameBase

A HEADERS frame and the field block it opens (RFC 9113 §6.2).

Construct it with the connection's shared HPACK decoder / encoder: the dynamic table spans the whole connection, so a per-frame codec decodes against the wrong table. When END_HEADERS is set the block is decoded immediately; otherwise append each CONTINUATION payload to raw_block and call parse_payload once the block is complete.

Decoding splits the result two ways. pseudo_headers maps PseudoHeaders to the received octets, which the request and response layers grade and turn into text only where they need it; headers is the ordinary field list as (name, value) byte pairs.

A block that breaks §8.1.2 / §8.2 does not raise — it sets malformed with a malformed_reason, so the caller can reset the stream while the connection-wide table stays in step with the peer's.

parse_payload()

Decode the field block, which may span several frames.

Padding and priority belong to the frame that carried the flags, and self.length is that frame's payload; everything past it was appended by CONTINUATION and is fragment throughout.

IncompleteFrameError

Bases: FrameFormatError

Fewer octets than a frame header, or than the payload its header declares (RFC 9113 §4.1).

Ping

Bases: FrameBase

A PING frame — connection liveness and round-trip measurement (RFC 9113 §6.7).

The payload is exactly eight octets; any other length raises FrameFormatError carrying FRAME_SIZE_ERROR. A reply repeats the payload unchanged with PingFrameFlags.ACK set.

PingFrameFlags

Bases: FrameFlags

The one flag a PING frame may carry (RFC 9113 §6.7): ACK marks the frame as the reply, whose payload must echo the ping's own.

Priority

Bases: FrameBase

A PRIORITY frame — a stream dependency and weight (RFC 9113 §6.3).

RFC 9113 §5.3 deprecates the scheme these fields express, but a peer may still send them, so they are parsed rather than refused. weight is the true weight in 1–256, one more than the octet on the wire; exclusion holds the top bit of the dependency field and dependent_stream the remaining 31.

PriorityUpdate

Bases: FrameBase

RFC 9218 §7.1 — PRIORITY_UPDATE frame (type 0x10).

Payload: 4-byte Prioritized Stream ID + ASCII Priority field value (e.g. u=3 or u=0, i). The server receives and logs the hint but does not reorder stream processing.

PseudoHeaders

Bases: StrEnum

RFC 7540 §8.1.2 pseudo-header field names.

PushPromise

Bases: FrameBase

RFC 7540 §6.6 — PUSH_PROMISE frame (type 0x05).

Sent on the initiating (parent) stream; carries the promised stream ID and the HPACK-encoded synthetic request headers for the pushed resource.

parse_payload()

Decode the promised block and discard it: the side effect on the connection-wide HPACK table is the point, and RFC 9113 §4.3 requires it even though the client acts on no push.

Splitting works as in Headers.parse_payload.

RstStream

Bases: FrameBase

A RST_STREAM frame — abrupt termination of one stream (RFC 9113 §6.4).

The payload is exactly four octets; any other length raises FrameFormatError carrying FRAME_SIZE_ERROR. error_code is an ErrorCodes member when the peer sent a registered code and a plain int when it did not, so read it as an integer rather than assuming the enum.

SettingFrame

Bases: FrameBase

A SETTINGS frame — connection-wide parameters (RFC 9113 §6.5).

Each identifier the peer sends becomes an attribute named for it (header_table_size, max_concurrent_streams, max_frame_size, …). An omitted setting leaves no attribute at all, so read one with getattr and supply the default the RFC names — initial_window_size is the exception, present as None until the peer sets it. An unknown identifier is ignored, as §6.5.2 requires.

payload property

The raw payload octets, where an identifier this build does not name survives.

settings property

The frame's entries as (identifier, value) pairs.

A payload that does not divide into six-octet entries yields the complete entries it holds.

SettingFrameFlags

Bases: FrameFlags

Flags a SETTINGS frame may carry (RFC 9113 §6.5).

INIT is the absence of ACK rather than a flag the RFC defines: it names the frame that carries settings, as opposed to the empty frame that acknowledges them.

WindowUpdate

Bases: FrameBase

A WINDOW_UPDATE frame — a flow-control credit (RFC 9113 §6.9).

window_size is the increment to add, not the resulting window, and it applies to the connection when stream_id is 0 and to that stream otherwise.

field_name_is_valid(name)

RFC 9113 §8.2.1 — is name a legal HTTP/2 field name?

The RFC 9110 §5.6.2 token alphabet, lowercased (§8.2), with the single leading colon that marks a pseudo-header field allowed. The alphabet is shared with HTTP/1.1: a separator is not a name octet on either transport, and a name that matched here but not there was a smuggling surface §8.2.1 names explicitly.

field_value_has_boundary_whitespace(value)

RFC 9113 §8.2.1 — SP or HTAB at either end of a field value.

Inside the value both octets stay legal, which is why this is a second question and not folded into field_value_is_valid: it is about the position, not the octet.

field_value_is_valid(value)

RFC 9110 §5.5 field-content — no control octet but HTAB, and no DEL.

§8.2.1 states the minimal form of this rule as a MUST and asks for the full HTTP definition as a SHOULD; HTTP/1.1 already refuses these octets, and a value is the same value on either transport.

no_hpack_context(frame, codec)

Refuse header framing without the connection-owned shared HPACK context.

Never substitute a private encoder or decoder (RFC 7541 §2.3, RFC 9113 §4.3).

parse_priority_field(s)

Parse an RFC 9218 Priority field value (a Structured Fields Dictionary).

"u=5, i" → {'urgency': 5, 'incremental': True}

Defaults per RFC 9218 §4.1: urgency=3, incremental=False. A value that fails strict RFC 9651 parsing is ignored entirely (RFC 9218 §5), and out-of-range or mistyped members are ignored individually (RFC 9218 §4) — either way the defaults apply.

serialize_field_block(payload, frame_type, flags, stream_id, max_frame_size)

Serialize one complete field block for an uninterrupted writer handoff.