# flare.http3.request_reader

> A module of flare_mlake's API.

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`

```mojo
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`

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

## Traits

### `Http3RequestEventHandler`

```mojo
undefined
```

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`

```mojo
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`

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

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

#### `on_trailers`

```mojo
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`

```mojo
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`

```mojo
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`

```mojo
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

### `H3_REQUEST_STATE_INIT`

```mojo
comptime H3_REQUEST_STATE_INIT = Int(0)
```

Awaiting the first HEADERS frame on this request stream.

### `H3_REQUEST_STATE_BODY`

```mojo
comptime H3_REQUEST_STATE_BODY = Int(1)
```

HEADERS received; reading DATA + optional trailers.

### `H3_REQUEST_STATE_TRAILERS`

```mojo
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.

### `H3_REQUEST_STATE_DONE`

```mojo
comptime H3_REQUEST_STATE_DONE = Int(3)
```

Stream is closed; further calls are no-ops.
