nhttp_h3 (nhttp_lib v1.0.0)

View Source

HTTP/3 protocol layer.

Pure functional HTTP/3 connection state machine (RFC 9114). Sits between the QUIC transport (nquic) and the application layer. The caller manages QUIC I/O and routes data to/from this module. Return values include actions that the caller must execute via nquic.

Usage

H3 = nhttp_h3:new(server, #{}),
{ok, H3_1, Actions} = nhttp_h3:init_local_streams(H3, #{
    control => CtrlId, encoder => EncId, decoder => DecId
}),
execute_actions(QConn, Actions),
loop(QConn, H3_1).

Sending bodies and trailers

The HTTP/3 send surface is frame-oriented. The canonical nhttp_lib:request/0 and nhttp_lib:response/0 maps can carry a body and trailers field as a convenience for "everything is in memory", but this layer does not consume those maps directly: the caller breaks the exchange into discrete frame sends. The pattern is:

  1. send_headers(Conn, StreamId, Headers, nofin) to emit pseudo-headers plus regular headers (HEADERS frame, QPACK-encoded).
  2. Zero or more send_data(Conn, StreamId, Chunk, nofin) calls.
  3. Either send_data(Conn, StreamId, FinalChunk, fin) to close on body, or send_headers(Conn, StreamId, Trailers, fin) to close on trailers.

send_response/4 is the convenience one-shot for "headers + complete body + END_STREAM" when the body is already a single iodata().

Summary

Functions

Register QUIC stream IDs for local unidirectional streams. Returns initial data to send on each (stream type prefix + settings on control).

Create a new HTTP/3 connection.

Process incoming data on any QUIC stream.

Encode and send data on a request stream.

Send GOAWAY on the control stream.

Encode and send headers on a request stream.

Encode and send a complete response (headers + body) on a request stream. Combines HEADERS and DATA frames into a single send_fin action, avoiding redundant stream lookups and state transitions compared to calling send_headers/4 then send_data/4 separately.

Record the peer address on the connection. Called once after the QUIC connection is established so server-built nhttp_lib:request/0 maps and client-built nhttp_lib:response/0 events carry the correct remote peer.

Notify that the peer opened a new stream.

Handle a peer stream reset.

Types

action()

-type action() ::
          {send, nhttp_lib:stream_id(), iodata()} |
          {send_fin, nhttp_lib:stream_id(), iodata()} |
          {close_connection, non_neg_integer(), binary()}.

conn()

-opaque conn()

error_code()

-type error_code() :: nhttp_lib:error_code().

event()

-type event() ::
          nhttp_lib:event_common() |
          {settings, h3_settings()} |
          {push_promise, nhttp_lib:stream_id(), non_neg_integer(), nhttp_lib:headers()}.

fin()

-type fin() :: nhttp_lib:fin().

h3_error()

-type h3_error() ::
          {connection_error, h3_error_code(), binary()} |
          {stream_error, nhttp_lib:stream_id(), h3_error_code(), binary()}.

h3_error_code()

-type h3_error_code() ::
          h3_no_error | h3_general_protocol_error | h3_internal_error | h3_stream_creation_error |
          h3_closed_critical_stream | h3_frame_unexpected | h3_frame_error | h3_excessive_load |
          h3_id_error | h3_settings_error | h3_missing_settings | h3_request_rejected |
          h3_request_cancelled | h3_request_incomplete | h3_message_error | h3_connect_error |
          h3_version_fallback | qpack_decompression_failed | qpack_encoder_stream_error |
          qpack_decoder_stream_error.

h3_settings()

-type h3_settings() :: nhttp_h3_frame:h3_settings().

role()

-type role() :: nhttp_lib:role().

Functions

init_local_streams/2

-spec init_local_streams(conn(),
                         #{control := nhttp_lib:stream_id(),
                           encoder := nhttp_lib:stream_id(),
                           decoder := nhttp_lib:stream_id()}) ->
                            {ok, conn(), [action()]}.

Register QUIC stream IDs for local unidirectional streams. Returns initial data to send on each (stream type prefix + settings on control).

new(Role, Settings)

-spec new(role(), h3_settings()) -> conn().

Create a new HTTP/3 connection.

recv/4

-spec recv(conn(), nhttp_lib:stream_id(), binary(), fin()) ->
              {ok, [event()], conn(), [action()]} | {error, h3_error()}.

Process incoming data on any QUIC stream.

send_data/4

-spec send_data(conn(), nhttp_lib:stream_id(), iodata(), fin()) ->
                   {ok, conn(), [action()]} | {error, h3_error()}.

Encode and send data on a request stream.

send_goaway/1

-spec send_goaway(conn()) -> {ok, conn(), [action()]}.

Send GOAWAY on the control stream.

send_headers/4

-spec send_headers(conn(), nhttp_lib:stream_id(), nhttp_lib:headers(), fin()) ->
                      {ok, conn(), [action()]} | {error, h3_error()}.

Encode and send headers on a request stream.

send_response/4

-spec send_response(conn(), nhttp_lib:stream_id(), nhttp_lib:headers(), iodata()) ->
                       {ok, conn(), [action()]} | {error, h3_error()}.

Encode and send a complete response (headers + body) on a request stream. Combines HEADERS and DATA frames into a single send_fin action, avoiding redundant stream lookups and state transitions compared to calling send_headers/4 then send_data/4 separately.

set_peer/2

-spec set_peer(conn(), nhttp_lib:peer()) -> conn().

Record the peer address on the connection. Called once after the QUIC connection is established so server-built nhttp_lib:request/0 maps and client-built nhttp_lib:response/0 events carry the correct remote peer.

stream_opened/3

-spec stream_opened(conn(), nhttp_lib:stream_id(), bidi | uni) -> {ok, conn()} | {error, h3_error()}.

Notify that the peer opened a new stream.

stream_reset(Conn, StreamId, ErrorCode)

-spec stream_reset(conn(), nhttp_lib:stream_id(), non_neg_integer()) -> {ok, [event()], conn()}.

Handle a peer stream reset.