livery_ws (livery v0.6.0)

View Source

WebSocket integration on top of the ws library.

A handler that wants to upgrade a request to WebSocket calls upgrade/3 inside its body. The function performs the protocol-specific handshake by dispatching to the adapter's accept_ws/4 helper. The return value is a sentinel response (status = 101, body = taken_over) that tells livery:emit/3 no further bytes need to be written: the stream/socket now belongs to the ws session.

my_ws_route(Req) ->
    livery_ws:upgrade(Req, my_chat_handler, #{}).

my_chat_handler is a module implementing the ws_handler behaviour (defined by erlang_ws).

WebSocket runs over H1 (plain Upgrade), H2 (RFC 8441 extended CONNECT, via livery_ws_h2), and H3 (RFC 9220 extended CONNECT, via livery_ws_h3).

Summary

Functions

Split upgrade Opts into the two handshake option maps the adapters need: {ValidateOpts, AcceptOpts}. ValidateOpts drives ws_hN_upgrade:validate_request/2 (subprotocol negotiation) and AcceptOpts drives ws:accept/6 (idle timeout). Both are #{} when the respective key is absent, which makes the calls equivalent to the /1 and /5 forms. Called by the H1/H2/H3 adapters.

Upgrade the current request to a WebSocket session.

Types

handler_module()

-type handler_module() :: module().

handler_opts()

-type handler_opts() :: term().

Functions

handshake_opts/1

-spec handshake_opts(handler_opts()) -> {map(), map()}.

Split upgrade Opts into the two handshake option maps the adapters need: {ValidateOpts, AcceptOpts}. ValidateOpts drives ws_hN_upgrade:validate_request/2 (subprotocol negotiation) and AcceptOpts drives ws:accept/6 (idle timeout). Both are #{} when the respective key is absent, which makes the calls equivalent to the /1 and /5 forms. Called by the H1/H2/H3 adapters.

upgrade(Req, HandlerMod, Opts)

Upgrade the current request to a WebSocket session.

HandlerMod must implement the ws_handler behaviour. Opts is a map forwarded as HMod:init(Req, Opts)'s second argument by the ws library. Two keys are also interpreted by the handshake (and left in Opts, so the handler still sees them):

  • subprotocols => [binary()] drives subprotocol negotiation: the first of these the client also offers is echoed in the response Sec-WebSocket-Protocol, and a client offering none of them is rejected. Omit to skip negotiation.
  • idle_timeout => timeout() overrides the session idle timeout (infinity never idle-closes). Omit for the ws default.

The handler's Req carries peer => {IpAddress, Port}, the client address from the socket (H1), the h2 connection (H2), or the QUIC connection (H3).

Returns a #livery_resp{} value:

  • status = 101, body = taken_over on a successful handshake. The adapter owns nothing further on this stream after this point.
  • status = 400 with a textual body when the inbound headers do not satisfy RFC 6455.
  • status = 501 when the adapter does not support WebSocket upgrades (H1, H2, and H3 all do).