livery_h1h2 (livery v0.9.2)
View SourceOne 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:
- Accepts, then hands the socket to a fresh process, so a stalled handshake never blocks the accept queue.
- Runs
ssl:handshake/2and readsssl:negotiated_protocol/1. - Calls
h2:serve_socket/2forh2, andh1:serve_socket/2forhttp/1.1and for a client that offered no ALPN at all. - Stays alive for the connection's lifetime:
serve_socket/2links 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
Types
-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()}.
-type listener() :: {pid(), inet:port_number()}.
-type protocol() :: h2 | http1.
-type state() :: #state{listen :: ssl:sslsocket() | undefined, port :: inet:port_number(), acceptors :: [pid()], conns :: #{pid() => true}, conn_args :: conn_args()}.
Functions
-spec init(listen_opts()) -> {ok, state()} | {stop, term()}.
-spec server_port(listener()) -> inet:port_number().
The port the listener bound to.
-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.
-spec start(atom() | undefined, listen_opts(), map()) -> {ok, listener()} | {error, term()}.
-spec stop(listener()) -> ok.
Stop the listener. Synchronous: closes the listen socket and every accepted connection before returning.
-spec stop_accepting(listener()) -> ok.
Stop accepting new connections while continuing to serve the
established ones. Call stop/1 afterwards to close them.