livery_h1h2 (livery v0.9.2)

View Source

One TLS listener serving HTTP/2 and HTTP/1.1, chosen per connection by ALPN.

Livery owns the listen socket here rather than delegating it to h1 or h2, because only the process that ran the handshake knows which protocol the client asked for. Per connection the listener:

  1. Accepts, then hands the socket to a fresh process, so a stalled handshake never blocks the accept queue.
  2. Runs ssl:handshake/2 and reads ssl:negotiated_protocol/1.
  3. Calls h2:serve_socket/2 for h2, and h1:serve_socket/2 for http/1.1 and for a client that offered no ALPN at all.
  4. Stays alive for the connection's lifetime: serve_socket/2 links the connection to its caller, so the process that dispatched is the one the connection dies with.

Requests dispatch through livery_h1 and livery_h2 exactly as on the dedicated listeners, so handlers, middleware, and livery_req:protocol/1 see the real per-connection protocol. The negotiated ALPN travels on the stream and comes back out of Adapter:peer_info/1.

The server prefers the protocols in the order alpn lists them, so the default [h2, http1] means h2 wins whenever a client offers both.

Summary

Functions

The port the listener bound to.

Start a multiplexed TLS listener.

Stop the listener. Synchronous: closes the listen socket and every accepted connection before returning.

Stop accepting new connections while continuing to serve the established ones. Call stop/1 afterwards to close them.

Types

conn_args()

-type conn_args() :: #{handshake_timeout := timeout(), h2 := map(), http1 := map(), no_alpn := map()}.

listen_opts()

-type listen_opts() ::
          #{port => inet:port_number(),
            ip => inet:ip_address(),
            inet6 => boolean(),
            cert => binary() | string(),
            key => binary() | string(),
            cacerts => [binary()],
            verify => verify_none | verify_peer,
            ssl_opts => [ssl:tls_server_option()],
            alpn => [protocol()],
            acceptors => pos_integer(),
            handshake_timeout => timeout(),
            stack := livery_middleware:stack(),
            handler := livery_middleware:handler(),
            atom() => term()}.

listener()

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

protocol()

-type protocol() :: h2 | http1.

state()

-type state() ::
          #state{listen :: ssl:sslsocket() | undefined,
                 port :: inet:port_number(),
                 acceptors :: [pid()],
                 conns :: #{pid() => true},
                 conn_args :: conn_args()}.

Functions

code_change/3

-spec code_change(term(), state(), term()) -> {ok, state()}.

handle_call/3

-spec handle_call(term(), {pid(), term()}, state()) -> {reply, term(), state()}.

handle_cast/2

-spec handle_cast(term(), state()) -> {noreply, state()}.

handle_info/2

-spec handle_info(term(), state()) -> {noreply, state()}.

init(Opts)

-spec init(listen_opts()) -> {ok, state()} | {stop, term()}.

server_port/1

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

The port the listener bound to.

start(Opts)

-spec start(listen_opts()) -> {ok, listener()} | {error, term()}.

Start a multiplexed TLS listener.

Opts must include cert, key, stack, and handler. port defaults to 0 (random port). Returns a handle for stop/1, stop_accepting/1, and server_port/1.

start(Name, Opts, StartOpts)

-spec start(atom() | undefined, listen_opts(), map()) -> {ok, listener()} | {error, term()}.

stop/1

-spec stop(listener()) -> ok.

Stop the listener. Synchronous: closes the listen socket and every accepted connection before returning.

stop_accepting/1

-spec stop_accepting(listener()) -> ok.

Stop accepting new connections while continuing to serve the established ones. Call stop/1 afterwards to close them.

terminate(Reason, State)

-spec terminate(term(), state()) -> ok.