flare_mlake · API · http3

flare.http3.request_reader

HTTP/3 request-stream reader -- sans-I/O state machine.

An HTTP/3 request lives on a single bidirectional QUIC stream: the client emits a HEADERS frame (QPACK-encoded request pseudo- headers + application headers), zero or more DATA frames carrying the request body, and optionally a trailing HEADERS frame for trailers. The stream's FIN bit closes the request side of the exchange.

This module ships the codec-side reader that turns a stream of bytes into a typed callback sequence on a caller-supplied :trait:Http3RequestEventHandler:

  • :meth:Http3RequestEventHandler.on_headers -- the first HEADERS frame has been parsed and QPACK-decoded.
  • :meth:Http3RequestEventHandler.on_data -- a DATA frame's payload is ready.
  • :meth:Http3RequestEventHandler.on_trailers -- the trailing HEADERS frame closing the request side has been parsed.
  • :meth:Http3RequestEventHandler.on_unknown_frame -- an unknown / grease frame type was parsed; receivers MUST ignore (RFC 9114 §7.2.8). The reader skips the payload bytes and fires the callback so the caller can log it.
  • :meth:Http3RequestEventHandler.on_protocol_error -- the byte stream is malformed (truncated varint, oversize length, QPACK decode failure, repeated HEADERS, an unknown frame declaring more than max_field_section_bytes); the caller surfaces this as an H3_FRAME_UNEXPECTED stream-level error to the QUIC peer. A QPACK decode failure is tagged QPACK_DECOMPRESSION_FAILED: :class:flare.http3.Http3Connection raises it as a connection error (RFC 9204 section 2.2.3).

The dispatcher entry point :func:feed_into[H] returns the number of bytes consumed. A return of 0 means NEEDS_MORE -- the buffer does not yet hold a complete next frame; the caller accumulates more bytes and calls again. The reader fires at most one callback per feed_into invocation.

Sans-I/O contract: the reader holds zero socket / QUIC references; it operates on byte spans that the QUIC stream reassembly layer hands it. The H3 server reactor wraps this in a per-stream loop that calls feed_into after every QUIC DATA chunk arrival.

References:

  • RFC 9114 §4 (HTTP Message Exchanges) + §7 (Frames).
  • RFC 9204 (QPACK) -- field-section decoder used for HEADERS.

Structs

Http3RequestReader

struct Http3RequestReader

Per-stream H3 request-side reader.

The reader is stateful: it tracks whether the initial HEADERS frame has been seen so a second HEADERS frame can be classified as either trailers (legal, after at least one DATA frame in practice but the spec allows zero) or a protocol error (e.g. when the state has already advanced to TRAILERS).

Implements Copyable, Movable

Fields
state: Int
max_field_section_bytes: UInt64
max_body_bytes: UInt64: Most request-body bytes accepted across all DATA frames.
body_bytes: UInt64: Request-body bytes delivered so far.
qpack_table: ArcPointer[QpackDynamicTable]: RFC 9204 dynamic table shared (by ArcPointer) from the owning :class:Http3Connection. Defaults to an empty (capacity-0) table so a standalone reader decodes static-only field sections identically to the static path; the connection injects its real table at :meth:Http3Connection.open_request_stream so dynamic references resolve.

new

def new(max_field_section_bytes: UInt64 = UInt64(8192), max_body_bytes: UInt64 = UInt64(10485760)) -> Self

Traits

Http3RequestEventHandler

Per-event callback contract :func:feed_into fires.

The dispatcher reads one wire frame at the start of the request-stream buffer and invokes exactly one callback per successful parse (HEADERS, DATA, TRAILERS, UNKNOWN_FRAME, or PROTOCOL_ERROR). NEEDS_MORE returns 0 without firing any callback -- the caller accumulates more bytes and re-invokes the dispatcher.

Callbacks own their payload arguments by value so the handler can stash them (e.g. into a per-stream request accumulator) without lifetime gymnastics.

Implements Movable

on_headers

def on_headers(mut self, headers: List[HpackHeader])

A complete first HEADERS frame has been parsed and QPACK-decoded. The reader's state advances to BODY.

on_data

def on_data(mut self, data: List[UInt8])

A complete DATA frame has been parsed. data carries the payload bytes.

on_trailers

def on_trailers(mut self, trailers: List[HpackHeader])

A trailing HEADERS frame closing the request side has been parsed. The reader's state advances to TRAILERS.

on_unknown_frame

def on_unknown_frame(mut self, type_id: UInt64)

An unknown / grease frame type fired. Receivers MUST ignore per RFC 9114 §7.2.8; the reader skips the payload bytes and fires this so the caller may log.

on_protocol_error

def on_protocol_error(mut self, message: String)

The byte stream is malformed (truncated varint, oversize length, QPACK decode failure, out-of-order frame). The reader's state has already advanced to DONE before this fires; the caller surfaces this as a stream-level error to the QUIC peer.

Functions

feed_into

def feed_into[H: Http3RequestEventHandler](mut reader: Http3RequestReader, buf: Span[UInt8], mut handler: H) -> Int

Try to parse the next H3 frame at the start of buf and fire the matching :trait:Http3RequestEventHandler callback.

Returns the number of bytes consumed:

  • 0 -- NEEDS_MORE: the buffer is truncated or the reader is already DONE. No callback fires; the caller accumulates more bytes and re-invokes the dispatcher.
  • > 0 -- exactly one callback fired (on_headers / on_data / on_trailers / on_unknown_frame / on_protocol_error). The caller advances its cursor by the returned count and re-invokes the dispatcher to drain the remainder.

Protocol errors advance the reader's state to DONE before the callback fires; subsequent feed_into calls return 0 and never fire another callback.

Aliases

comptime H3_REQUEST_STATE_INIT = Int(0)

Awaiting the first HEADERS frame on this request stream.

comptime H3_REQUEST_STATE_BODY = Int(1)

HEADERS received; reading DATA + optional trailers.

comptime H3_REQUEST_STATE_TRAILERS = Int(2)

Trailers received; the next event must be NEEDS_MORE or end-of-stream signalled by the caller. Receiving any further frame is a protocol error.

comptime H3_REQUEST_STATE_DONE = Int(3)

Stream is closed; further calls are no-ops.