h2 (h2 v0.10.0)
View SourceHTTP/2 Public API
This module provides the public API for HTTP/2 client and server operations. It wraps the h2_connection state machine with a clean interface.
Client Usage
%% Connect to a server
{ok, Conn} = h2:connect("example.com", 443, #{}).
%% Send a request
{ok, StreamId} = h2:request(Conn, <<"GET">>, <<"/">>, [
{<<"host">>, <<"example.com">>}
]).
%% Receive response (messages sent to caller)
receive
{h2, Conn, {response, StreamId, Status, Headers}} ->
io:format("Status: ~p~n", [Status]);
{h2, Conn, {data, StreamId, Data, IsFin}} ->
io:format("Data: ~p~n", [Data])
end.
%% Close connection
ok = h2:close(Conn).Server Usage
%% Start server
{ok, Server} = h2:start_server(8443, #{
cert => "server.pem",
key => "server-key.pem",
handler => fun(Conn, StreamId, Method, Path, Headers) ->
h2:send_response(Conn, StreamId, 200, [{<<"content-type">>, <<"text/plain">>}]),
h2:send_data(Conn, StreamId, <<"Hello World!">>, true)
end
}).
%% Stop server
ok = h2:stop_server(Server).Event Messages
The owner process receives the following messages:
Client: - {h2, Conn, {response, StreamId, Status, Headers}} - {h2, Conn, {data, StreamId, Data, IsFin}} - {h2, Conn, {trailers, StreamId, Trailers}} - {h2, Conn, {stream_reset, StreamId, ErrorCode}} - {h2, Conn, {goaway, LastStreamId, ErrorCode}} - {h2, Conn, closed}
Server handler receives direct calls.
Summary
Functions
Cancel a stream.
Cancel a stream with a specific error code.
Close the connection immediately.
Connect to an HTTP/2 server. Uses TLS by default on port 443.
Connect to an HTTP/2 server with options.
Acknowledge consumption of ByteCount received bytes on a manual flow-control stream, replenishing its receive window. Receive-side backpressure: a handler calls this only after processing data, gating the peer's WINDOW_UPDATE on consumer progress. No-op on auto-mode streams.
Transfer connection ownership.
Get peer settings.
Get local settings.
Initiate graceful connection shutdown.
Initiate connection shutdown with error code.
Send an HTTP/2 request with pre-built headers (matches quic_h3:request/2). Headers should include pseudo-headers (:method, :path, :scheme, :authority).
Send an HTTP/2 request. For body-less requests (GET, HEAD, etc.), sends HEADERS with END_STREAM. For CONNECT (RFC 7540 §8.3) leaves the stream open so the caller can send tunnel bytes via send_data.
Send an HTTP/2 request with body. Sends HEADERS without END_STREAM, then sends DATA with END_STREAM.
Send a complete response (headers + full body) in a single call. This is the fast path for the common request/response case: one message to the connection and one socket write (HEADERS coalesced with DATA), versus the two round-trips of send_response/4 followed by send_data/4. Falls back to the granular path transparently when the response cannot be coalesced (oversized headers/body, CONNECT tunnels).
Send data on a stream.
Send data on a stream with end_stream flag.
Send data with per-call options. Pass #{block => Timeout} (ms or infinity) to block until the peer's window accepts the data, returning ok, or {error, timeout} if the window does not open in time (the data may still be queued; the caller should slow down or cancel the stream).
Send an HTTP/2 response (server mode).
Send trailers on a stream.
Return the TCP port the server is actually listening on.
Register a pid to receive a stream's events. Routes every event for the stream — {response,...}, {data,...}, {trailers,...}, {informational,...}, {stream_reset,...} — to the handler pid as {h2, Conn, Event}. By default the connection replays any events buffered before the handler was registered, in arrival order, so a response/trailers that raced ahead of registration is never dropped to the owner. The call returns ok. Pass #{drain_buffer => true} to instead get the buffered DATA back in the reply ({ok, [{Data, Fin}, ...]}) and forward it yourself (kept for the WebSocket/MASQUE tunnel callers).
Start an HTTP/2 server.
Start a named HTTP/2 server (matches quic_h3:start_server/3).
Stop an HTTP/2 server.
Wait for a client connection to reach the connected state.
Types
-type connect_opts() :: #{transport => tcp | ssl, ssl_opts => [ssl:tls_client_option()], cert => binary() | string(), key => binary() | string(), cacerts => [binary()], verify => verify_none | verify_peer, settings => h2_settings:settings(), timeout => timeout(), connect_timeout => timeout(), sync => boolean()}.
-type connection() :: pid().
-type error_code() :: h2_error:error_code().
-type server_opts() :: #{transport => ssl | tcp, cert => binary() | string(), key => binary() | string(), cacerts => [binary()], verify => verify_none | verify_peer, ssl_opts => [ssl:tls_option()], ip => inet:ip_address(), inet6 => boolean(), handler := fun((connection(), stream_id(), binary(), binary(), headers()) -> any()), settings => h2_settings:settings(), acceptors => pos_integer(), enable_connect_protocol => boolean()}.
-type server_ref() :: {pid(), reference(), inet:port_number()}.
-type status() :: 100..599.
-type stream_id() :: non_neg_integer().
Functions
-spec cancel(connection(), stream_id()) -> ok | {error, term()}.
Cancel a stream.
-spec cancel(connection(), stream_id(), error_code()) -> ok | {error, term()}.
Cancel a stream with a specific error code.
-spec cancel_stream(connection(), stream_id()) -> ok | {error, term()}.
-spec cancel_stream(connection(), stream_id(), error_code()) -> ok | {error, term()}.
-spec close(connection()) -> ok.
Close the connection immediately.
-spec connect(string() | binary(), inet:port_number()) -> {ok, connection()} | {error, term()}.
Connect to an HTTP/2 server. Uses TLS by default on port 443.
-spec connect(string() | binary(), inet:port_number(), connect_opts()) -> {ok, connection()} | {error, term()}.
Connect to an HTTP/2 server with options.
-spec consume(connection(), stream_id(), non_neg_integer()) -> ok | {error, term()}.
Acknowledge consumption of ByteCount received bytes on a manual flow-control stream, replenishing its receive window. Receive-side backpressure: a handler calls this only after processing data, gating the peer's WINDOW_UPDATE on consumer progress. No-op on auto-mode streams.
-spec controlling_process(connection(), pid()) -> ok | {error, term()}.
Transfer connection ownership.
-spec get_peer_settings(connection()) -> h2_settings:settings().
Get peer settings.
-spec get_settings(connection()) -> h2_settings:settings().
Get local settings.
-spec goaway(connection()) -> ok | {error, term()}.
Initiate graceful connection shutdown.
-spec goaway(connection(), error_code()) -> ok | {error, term()}.
Initiate connection shutdown with error code.
-spec request(connection(), headers()) -> {ok, stream_id()} | {error, term()}.
Send an HTTP/2 request with pre-built headers (matches quic_h3:request/2). Headers should include pseudo-headers (:method, :path, :scheme, :authority).
-spec request(connection(), headers(), map()) -> {ok, stream_id()} | {error, term()}.
Send an HTTP/2 request. For body-less requests (GET, HEAD, etc.), sends HEADERS with END_STREAM. For CONNECT (RFC 7540 §8.3) leaves the stream open so the caller can send tunnel bytes via send_data.
-spec request(connection(), binary(), binary(), headers(), binary()) -> {ok, stream_id()} | {error, term()}.
Send an HTTP/2 request with body. Sends HEADERS without END_STREAM, then sends DATA with END_STREAM.
Send a complete response (headers + full body) in a single call. This is the fast path for the common request/response case: one message to the connection and one socket write (HEADERS coalesced with DATA), versus the two round-trips of send_response/4 followed by send_data/4. Falls back to the granular path transparently when the response cannot be coalesced (oversized headers/body, CONNECT tunnels).
-spec send_data(connection(), stream_id(), binary()) -> ok | {error, term()}.
Send data on a stream.
-spec send_data(connection(), stream_id(), binary(), boolean()) -> ok | {error, term()}.
Send data on a stream with end_stream flag.
Non-blocking backpressure: when the peer's send window is exhausted the data is buffered, and {error, send_buffer_full} is returned once the buffer would exceed the per-stream cap, so the caller backs off instead of growing memory without bound. For a blocking variant see send_data/5.
Send data with per-call options. Pass #{block => Timeout} (ms or infinity) to block until the peer's window accepts the data, returning ok, or {error, timeout} if the window does not open in time (the data may still be queued; the caller should slow down or cancel the stream).
-spec send_response(connection(), stream_id(), status(), headers()) -> ok | {error, term()}.
Send an HTTP/2 response (server mode).
-spec send_trailers(connection(), stream_id(), headers()) -> ok | {error, term()}.
Send trailers on a stream.
-spec server_port(server_ref()) -> inet:port_number().
Return the TCP port the server is actually listening on.
-spec set_stream_handler(connection(), stream_id(), pid()) -> ok | {ok, [{binary(), boolean()}]} | {error, term()}.
Register a pid to receive a stream's events. Routes every event for the stream — {response,...}, {data,...}, {trailers,...}, {informational,...}, {stream_reset,...} — to the handler pid as {h2, Conn, Event}. By default the connection replays any events buffered before the handler was registered, in arrival order, so a response/trailers that raced ahead of registration is never dropped to the owner. The call returns ok. Pass #{drain_buffer => true} to instead get the buffered DATA back in the reply ({ok, [{Data, Fin}, ...]}) and forward it yourself (kept for the WebSocket/MASQUE tunnel callers).
To avoid the registration race entirely, set the handler at stream creation with h2:request(Conn, Headers, #{handler => Pid}).
Backpressure: by default incoming DATA replenishes the receive window on dispatch, so a slow handler's mailbox can grow unbounded. Pass #{flow_control => manual} (here or at request time) to gate window replenishment on consume/3 — the handler calls h2:consume(Conn, StreamId, N) after processing N bytes, bounding in-flight data to one window.
-spec start_server(inet:port_number(), server_opts()) -> {ok, server_ref()} | {error, term()}.
Start an HTTP/2 server.
-spec start_server(atom(), inet:port_number(), server_opts()) -> {ok, server_ref()} | {error, term()}.
Start a named HTTP/2 server (matches quic_h3:start_server/3).
-spec stop_server(server_ref()) -> ok.
Stop an HTTP/2 server.
-spec unset_stream_handler(connection(), stream_id()) -> ok.
-spec wait_connected(connection()) -> ok | {error, term()}.
Wait for a client connection to reach the connected state.
-spec wait_connected(connection(), timeout()) -> ok | {error, term()}.