nhttp_h2 (nhttp_lib v1.0.0)

View Source

HTTP/2 protocol layer.

This module implements RFC 9113 HTTP/2 connection and stream state machines. It sits between the framing layer (nhttp_h2_frame) and the application layer, providing:

  • Connection lifecycle management (preface, settings, shutdown)
  • Stream state machine (RFC 9113 Section 5.1)
  • Flow control (connection and stream level)
  • Header block assembly (CONTINUATION handling)
  • Error handling (connection vs stream errors)

Usage

Conn0 = nhttp_h2:new(client),
Preface = nhttp_h2:preface(Conn0),
ok = ssl:send(Socket, Preface),

{ok, Events, Conn1} = nhttp_h2:recv(Conn0, Data),
lists:foreach(fun handle_event/1, Events),

{ok, Conn2, Frames} = nhttp_h2:send_headers(Conn1, StreamId, Headers, fin),
ok = ssl:send(Socket, Frames).

Sending bodies and trailers

The HTTP/2 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 + optional CONTINUATION).
  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.

For a one-shot send when the body is already a single iodata(), call send_headers/4 with nofin followed by send_data/4 with fin. Flow-control and END_STREAM ride on the DATA frame.

Summary

Functions

Create a new HTTP/2 connection with default settings.

Create a new HTTP/2 connection with custom settings.

Open a new stream and return its ID.

Generate the connection preface for this role. Client sends: magic + SETTINGS. Server sends: SETTINGS.

Process incoming data and return events. May return frames to send (e.g., SETTINGS_ACK, PING_ACK, WINDOW_UPDATE).

Send DATA frame(s).

Send GOAWAY to initiate graceful shutdown.

Send HEADERS frame for a new request/response or trailers.

Send PING frame with caller-supplied 8-byte opaque data. The caller is responsible for generating the opaque value (e.g. via crypto:strong_rand_bytes(8)) and matching it against the PING_ACK event.

Send RST_STREAM to cancel a stream.

Send WINDOW_UPDATE for connection or stream.

Record the peer address on the connection. Called once after the socket is accepted (or connected) so server-built nhttp_lib:request/0 maps and client-built nhttp_lib:response/0 events carry the correct remote peer.

Types

conn()

-opaque conn()

error_code()

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

event()

-type event() ::
          nhttp_lib:event_common() |
          {stream_closed, nhttp_lib:stream_id(), error_code()} |
          {stream_refused, nhttp_lib:stream_id()} |
          {window_update, nhttp_lib:stream_id(), pos_integer()} |
          {settings, settings()} |
          settings_ack |
          {ping, binary()} |
          {ping_ack, binary()}.

fin()

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

priority()

-type priority() ::
          #{exclusive := boolean(), stream_dependency := nhttp_lib:stream_id(), weight := 1..256}.

recv_result()

-type recv_result() ::
          {ok, [event()], conn()} |
          {ok, [event()], conn(), iodata()} |
          {error, nhttp_h2_frame:decode_error()}.

role()

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

send_error()

-type send_error() ::
          connection_closing |
          {unknown_stream, nhttp_lib:stream_id()} |
          {stream_closed, nhttp_lib:stream_id()} |
          {stream_error, nhttp_lib:stream_id(), error_code(), binary()}.

send_result()

-type send_result() ::
          {ok, conn()} |
          {ok, conn(), iodata()} |
          {partial, conn(), iodata(), binary(), fin(), Window :: integer()} |
          {error, send_error()}.

settings()

-type settings() ::
          #{header_table_size => non_neg_integer(),
            enable_push => boolean(),
            max_concurrent_streams => pos_integer() | infinity,
            initial_window_size => 1..2147483647,
            max_frame_size => 16384..16777215,
            max_header_list_size => pos_integer() | infinity,
            enable_connect_protocol => boolean()}.

stream_state()

-type stream_state() ::
          idle | reserved_local | reserved_remote | open | half_closed_local | half_closed_remote |
          closed.

Functions

new(Role)

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

Create a new HTTP/2 connection with default settings.

new(Role, LocalSettings)

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

Create a new HTTP/2 connection with custom settings.

open_stream/1

-spec open_stream(conn()) ->
                     {ok, nhttp_lib:stream_id(), conn()} |
                     {error, connection_closing | max_streams_reached}.

Open a new stream and return its ID.

preface/1

-spec preface(conn()) -> iodata().

Generate the connection preface for this role. Client sends: magic + SETTINGS. Server sends: SETTINGS.

recv/2

-spec recv(conn(), binary()) -> recv_result().

Process incoming data and return events. May return frames to send (e.g., SETTINGS_ACK, PING_ACK, WINDOW_UPDATE).

send_data/4

-spec send_data(conn(), nhttp_lib:stream_id(), iodata(), fin()) -> send_result().

Send DATA frame(s).

send_goaway/3

-spec send_goaway(conn(), error_code(), binary()) -> send_result().

Send GOAWAY to initiate graceful shutdown.

send_headers/4

-spec send_headers(conn(), nhttp_lib:stream_id(), nhttp_lib:headers(), fin()) -> send_result().

Send HEADERS frame for a new request/response or trailers.

send_ping/2

-spec send_ping(conn(), <<_:64>>) -> send_result().

Send PING frame with caller-supplied 8-byte opaque data. The caller is responsible for generating the opaque value (e.g. via crypto:strong_rand_bytes(8)) and matching it against the PING_ACK event.

send_rst_stream/3

-spec send_rst_stream(conn(), nhttp_lib:stream_id(), error_code()) -> send_result().

Send RST_STREAM to cancel a stream.

send_window_update/3

-spec send_window_update(conn(), nhttp_lib:stream_id() | connection, pos_integer()) -> send_result().

Send WINDOW_UPDATE for connection or stream.

set_peer/2

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

Record the peer address on the connection. Called once after the socket is accepted (or connected) so server-built nhttp_lib:request/0 maps and client-built nhttp_lib:response/0 events carry the correct remote peer.