livery_service (livery v0.9.2)

View Source

Service runtime.

Brings up H3 on UDP, H2 on TLS, and H1 on TCP under one supervisor, sharing one router/middleware/handler. Optionally advertises Alt-Svc on H1 and H2 responses so clients race up to H3.

The https listener is h2-only by default; alpn => [h2, http1] makes it serve HTTP/2 and HTTP/1.1 from the same port, chosen per connection.

Configuration map:

livery:start_service(#{
    host       => <<"example.com">>,
    http3      => #{port => 443, cert => Cert, key => Key},
    https      => #{port => 443, cert => Cert, key => Key,
                    alpn => [h2, http1]},
    http       => #{port => 80},
    handler    => fun handler/1,
    middleware => Stack,
    alt_svc    => advertise
}).

Supply exactly one of handler (a single catch-all) or router (a compiled livery_router the service dispatches through, via livery:router_handler/1).

Returns {ok, ServicePid}. The service pid owns the listeners and shuts them down when stopped via livery:stop_service/1. A crash takes them all down together. For a polite shutdown that lets in-flight requests finish, use livery:drain/1,2.

Summary

Functions

Start a service from a config map.

Stop a running service.

Stop the service's listeners (no new connections) while leaving the gen_server and any in-flight requests running. Used by livery_drain to begin a graceful shutdown.

Return the ports the service is bound to, by protocol. Keys are present only for protocols that were configured, and each maps to every port serving that protocol: an ALPN listener puts one port under both h1 and h2, and a cleartext http listener alongside it gives h1 two.

Types

listener_opts()

-type listener_opts() ::
          #{name => atom(),
            port => inet:port_number(),
            ip => inet:ip_address(),
            inet6 => boolean(),
            transport => tcp | ssl,
            cert => binary() | string(),
            key => binary() | string() | term(),
            cacerts => [binary()],
            verify => verify_none | verify_peer,
            ssl_opts => [ssl:tls_server_option()],
            alpn => [livery_h1h2:protocol()],
            acceptors => pos_integer(),
            handshake_timeout => timeout(),
            idle_timeout => timeout(),
            request_timeout => timeout(),
            max_keepalive_requests => pos_integer() | infinity,
            enable_connect_protocol => boolean(),
            max_body => non_neg_integer() | infinity,
            sni_callback =>
                fun((binary() | undefined) ->
                        {ok, #{cert := binary(), key := term(), cert_chain => [binary()]}} |
                        {error, term()}),
            settings => map(),
            quic_opts => map(),
            early_response_drain => 0 | {non_neg_integer() | infinity, non_neg_integer() | infinity},
            lingering_timeout => timeout(),
            config => term()}.

service_opts()

-type service_opts() ::
          #{host => binary(),
            http => listener_opts(),
            https => listener_opts(),
            http3 => listener_opts(),
            handler => livery_middleware:handler(),
            router => livery_router:router(),
            middleware => livery_middleware:stack(),
            config => term(),
            alt_svc => advertise | none}.

Functions

code_change/3

-spec code_change(term(),
                  #state{listeners ::
                             [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                        ref :: term(),
                                        port :: inet:port_number(),
                                        protocols :: [h1 | h2 | h3]}]},
                  term()) ->
                     {ok,
                      #state{listeners ::
                                 [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                            ref :: term(),
                                            port :: inet:port_number(),
                                            protocols :: [h1 | h2 | h3]}]}}.

handle_call/3

-spec handle_call(term(),
                  {pid(), term()},
                  #state{listeners ::
                             [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                        ref :: term(),
                                        port :: inet:port_number(),
                                        protocols :: [h1 | h2 | h3]}]}) ->
                     {reply,
                      term(),
                      #state{listeners ::
                                 [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                            ref :: term(),
                                            port :: inet:port_number(),
                                            protocols :: [h1 | h2 | h3]}]}}.

handle_cast/2

-spec handle_cast(term(),
                  #state{listeners ::
                             [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                        ref :: term(),
                                        port :: inet:port_number(),
                                        protocols :: [h1 | h2 | h3]}]}) ->
                     {noreply,
                      #state{listeners ::
                                 [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                            ref :: term(),
                                            port :: inet:port_number(),
                                            protocols :: [h1 | h2 | h3]}]}}.

handle_info/2

-spec handle_info(term(),
                  #state{listeners ::
                             [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                        ref :: term(),
                                        port :: inet:port_number(),
                                        protocols :: [h1 | h2 | h3]}]}) ->
                     {noreply,
                      #state{listeners ::
                                 [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                            ref :: term(),
                                            port :: inet:port_number(),
                                            protocols :: [h1 | h2 | h3]}]}}.

init(Opts)

-spec init(service_opts()) ->
              {ok,
               #state{listeners ::
                          [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                     ref :: term(),
                                     port :: inet:port_number(),
                                     protocols :: [h1 | h2 | h3]}]}} |
              {stop, term()}.

start_link(Opts)

-spec start_link(service_opts()) -> {ok, pid()} | {error, term()}.

Start a service from a config map.

stop(Pid)

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

Stop a running service.

stop_accepting(Pid)

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

Stop the service's listeners (no new connections) while leaving the gen_server and any in-flight requests running. Used by livery_drain to begin a graceful shutdown.

terminate(Reason, State)

-spec terminate(term(),
                #state{listeners ::
                           [#listener{mod :: livery_h1 | livery_h2 | livery_h3 | livery_h1h2,
                                      ref :: term(),
                                      port :: inet:port_number(),
                                      protocols :: [h1 | h2 | h3]}]}) ->
                   ok.

which_listeners(Pid)

-spec which_listeners(pid()) -> #{h1 | h2 | h3 => [inet:port_number()]}.

Return the ports the service is bound to, by protocol. Keys are present only for protocols that were configured, and each maps to every port serving that protocol: an ALPN listener puts one port under both h1 and h2, and a cleartext http listener alongside it gives h1 two.