h1 (h1 v0.2.0)

View Source

HTTP/1.1 public API.

Mirrors the surface of h2 (HTTP/2) and quic_h3 (HTTP/3) so applications can swap protocols without rewriting call sites.

Client

  {ok, Conn} = h1:connect("example.com", 80, #{}).
  {ok, StreamId} = h1:request(Conn, <<"GET">>, <<"/">>,
                              [{<<"host">>, <<"example.com">>}]).
  receive
      {h1, Conn, {response, StreamId, Status, _Headers}} -> ok
  end.
  ok = h1:close(Conn).

Server

  {ok, S} = h1:start_server(8080, #{
      transport => tcp,
      handler => fun(Conn, Id, _Method, _Path, _Hs) ->
          h1:send_response(Conn, Id, 200, [{<<"content-length">>, <<"2">>}]),
          h1:send_data(Conn, Id, <<"ok">>, true)
      end}).

Summary

Functions

Server: reply 200 Connection Established to a classic HTTP/1.1 CONNECT (RFC 9110 §9.3.6, RFC 9112 §3.2.3 authority-form request-target) and take ownership of the raw socket. Mirror of accept_upgrade/3 for the 101 Switching Protocols case, but writes status 200 and injects no Connection/Upgrade headers so bytes after CRLF belong to the tunnel.

Server: reply 101 Switching Protocols to an upgrade request.

Server: send 100 Continue to a client waiting on Expect.

Toggle request pipelining on a client connection.

Send a request using h2-compatible pseudo-headers. The list may contain :method, :path, :authority; they're translated into the HTTP/1.1 request line + Host header.

Client: send an Upgrade request and wait for 101 Switching Protocols.

Types

connect_opts/0

-type connect_opts() ::
          #{transport => tcp | ssl,
            ssl_opts => [ssl:tls_client_option()],
            connect_timeout => timeout(),
            timeout => timeout(),
            pipeline => boolean(),
            max_keepalive_requests => pos_integer(),
            idle_timeout => timeout(),
            request_timeout => timeout()}.

connection/0

-type connection() :: pid().

headers/0

-type headers() :: [{binary(), binary()}].

server_opts/0

-type server_opts() ::
          #{transport => tcp | ssl,
            cert => binary() | string(),
            key => binary() | string(),
            cacerts => [binary()],
            handler :=
                fun((connection(), stream_id(), binary(), binary(), headers()) -> any()) | module(),
            acceptors => pos_integer(),
            handshake_timeout => timeout(),
            idle_timeout => timeout(),
            request_timeout => timeout(),
            max_keepalive_requests => pos_integer()}.

server_ref/0

-type server_ref() :: {pid(), reference(), inet:port_number()}.

status/0

-type status() :: 100..599.

stream_id/0

-type stream_id() :: non_neg_integer().

Functions

accept_connect(Conn, StreamId, ExtraHeaders)

-spec accept_connect(connection(), stream_id(), headers()) ->
                        {ok, gen_tcp | ssl, term(), binary()} | {error, term()}.

Server: reply 200 Connection Established to a classic HTTP/1.1 CONNECT (RFC 9110 §9.3.6, RFC 9112 §3.2.3 authority-form request-target) and take ownership of the raw socket. Mirror of accept_upgrade/3 for the 101 Switching Protocols case, but writes status 200 and injects no Connection/Upgrade headers so bytes after CRLF belong to the tunnel.

accept_connect(Conn, StreamId, ExtraHeaders, Timeout)

-spec accept_connect(connection(), stream_id(), headers(), timeout()) ->
                        {ok, gen_tcp | ssl, term(), binary()} | {error, term()}.

accept_upgrade(Conn, StreamId, ExtraHeaders)

-spec accept_upgrade(connection(), stream_id(), headers()) -> {ok, term(), binary()} | {error, term()}.

Server: reply 101 Switching Protocols to an upgrade request.

cancel(Conn, StreamId)

-spec cancel(connection(), stream_id()) -> ok | {error, term()}.

cancel(Conn, StreamId, Reason)

-spec cancel(connection(), stream_id(), term()) -> ok | {error, term()}.

cancel_stream(Conn, StreamId)

-spec cancel_stream(connection(), stream_id()) -> ok | {error, term()}.

cancel_stream(Conn, StreamId, Reason)

-spec cancel_stream(connection(), stream_id(), term()) -> ok | {error, term()}.

close(Conn)

-spec close(connection()) -> ok.

connect(Host, Port)

-spec connect(string() | binary(), inet:port_number()) -> {ok, connection()} | {error, term()}.

connect(Host, Port, Opts)

-spec connect(string() | binary(), inet:port_number(), connect_opts()) ->
                 {ok, connection()} | {error, term()}.

continue(Conn, StreamId)

-spec continue(connection(), stream_id()) -> ok | {error, term()}.

Server: send 100 Continue to a client waiting on Expect.

controlling_process(Conn, Pid)

-spec controlling_process(connection(), pid()) -> ok | {error, term()}.

get_peer_settings(Conn)

-spec get_peer_settings(connection()) -> map().

get_settings(Conn)

-spec get_settings(connection()) -> map().

goaway(Conn)

-spec goaway(connection()) -> ok | {error, term()}.

goaway(Conn, Reason)

-spec goaway(connection(), term()) -> ok | {error, term()}.

pipeline(Conn, Enabled)

-spec pipeline(connection(), boolean()) -> ok | {error, term()}.

Toggle request pipelining on a client connection.

request(Conn, Headers)

-spec request(connection(), headers()) -> {ok, stream_id()} | {error, term()}.

Send a request using h2-compatible pseudo-headers. The list may contain :method, :path, :authority; they're translated into the HTTP/1.1 request line + Host header.

request(Conn, Headers, Opts)

-spec request(connection(), headers(), map()) -> {ok, stream_id()} | {error, term()}.

request(Conn, Method, Path, Headers)

-spec request(connection(), binary(), binary(), headers()) -> {ok, stream_id()} | {error, term()}.

request(Conn, Method, Path, Headers, Body)

-spec request(connection(), binary(), binary(), headers(), binary()) ->
                 {ok, stream_id()} | {error, term()}.

send_data(Conn, StreamId, Data)

-spec send_data(connection(), stream_id(), binary()) -> ok | {error, term()}.

send_data(Conn, StreamId, Data, EndStream)

-spec send_data(connection(), stream_id(), binary(), boolean()) -> ok | {error, term()}.

send_response(Conn, StreamId, Status, Headers)

-spec send_response(connection(), stream_id(), status(), headers()) -> ok | {error, term()}.

send_trailers(Conn, StreamId, Trailers)

-spec send_trailers(connection(), stream_id(), headers()) -> ok | {error, term()}.

server_port(_)

-spec server_port(server_ref()) -> inet:port_number().

set_stream_handler(Conn, StreamId, Pid)

-spec set_stream_handler(connection(), stream_id(), pid()) -> ok | {error, term()}.

set_stream_handler(Conn, StreamId, Pid, Opts)

-spec set_stream_handler(connection(), stream_id(), pid(), map()) -> ok | {error, term()}.

start_server(Port, Opts)

-spec start_server(inet:port_number(), server_opts()) -> {ok, server_ref()} | {error, term()}.

start_server(Name, Port, Opts)

-spec start_server(atom(), inet:port_number(), server_opts()) -> {ok, server_ref()} | {error, term()}.

stop_server(_)

-spec stop_server(server_ref()) -> ok.

unset_stream_handler(Conn, StreamId)

-spec unset_stream_handler(connection(), stream_id()) -> ok.

upgrade(Conn, Protocol, Headers)

-spec upgrade(connection(), binary(), headers()) ->
                 {ok, stream_id(), term(), binary(), headers()} | {error, term()}.

Client: send an Upgrade request and wait for 101 Switching Protocols.

upgrade(Conn, Protocol, Headers, Timeout)

-spec upgrade(connection(), binary(), headers(), timeout()) ->
                 {ok, stream_id(), term(), binary(), headers()} | {error, term()}.

wait_connected(Conn)

-spec wait_connected(connection()) -> ok | {error, term()}.

wait_connected(Conn, Timeout)

-spec wait_connected(connection(), timeout()) -> ok | {error, term()}.