arizona_roadrunner_server (arizona_roadrunner v0.1.0)

View Source

Boots a roadrunner listener wired up with Arizona routes: the roadrunner arizona_server adapter.

start/2 compiles the route list, stashes the originals so they can be re-compiled later, configures this listener's own dispatch persistent term ({arizona_roadrunner_dispatch, Name}), and launches a roadrunner listener under roadrunner_sup via roadrunner:start_listener/2.

recompile_routes/0 rebuilds every Arizona-managed dispatch by walking persistent terms tagged with the routes key. The dev hot reloader calls this after a successful recompile so new or changed routes take effect without restarting the listener.

Options

  • routes -- list of arizona_roadrunner_router:route/0 (required)
  • error_page -- {Module, Function} to render the dev error page (default {arizona_error_page, render})
  • scheme -- http (default) or https
  • tls -- [ssl:tls_server_option()]. Supplying it turns the listener into an HTTPS one, so scheme => https is optional sugar; pairing it with an explicit scheme => http is a contradiction and raises tls_with_http_scheme
  • transport_opts -- [{port, Port}] sugar for the listen port. Only port is accepted; any other entry errors (unsupported_transport_opt) rather than being silently dropped -- roadrunner takes other listener/socket options (ip, socket_backlog, ...) directly in proto_opts. Port precedence: transport_opts port, then proto_opts.port, then 4040
  • proto_opts -- map of roadrunner listener opts merged on top of the dispatch (e.g. max_clients, max_content_length, request_timeout, tls, slot_reconciliation; since roadrunner 0.9.0 also the per-connection memory bounds -- a top-level recv_buffer, and under ws the per-session recv_buffer and hibernate_after to park idle WebSocket sessions -- plus tls_handshake_timeout)
  • compress -- true (default) or false. When true, roadrunner_compress is attached to live and asset routes so HTML pages and static assets above the threshold are gzip/deflate encoded per Accept-Encoding. The dev SSE reload stream and the WebSocket upgrade response are intentionally not compressed. Set to false if you have an upstream proxy doing compression already -- and note the one thing that still compresses under false: a route explicitly listing arizona_middleware:compress() in its middlewares (route-level explicit beats the listener default). Behind a compressing proxy, leave compress() out of routes too.

Any option value (port, scheme, tls cert paths, proto_opts tunables) may be an env-var reference resolved at startup -- {env, "PORT", 8080} or the required {env, "PORT"}. See m:arizona_config.

Enabling HTTP/2

Roadrunner ships with HTTP/2 support (RFC 9113, h2spec-compliant). Opt in via proto_opts.protocols. Browsers require TLS + ALPN for HTTP/2, so pair it with scheme => https and a tls config:

arizona_roadrunner_server:start(arizona_http, #{
    routes => Routes,
    scheme => https,
    tls => [{certfile, "priv/cert.pem"}, {keyfile, "priv/key.pem"}],
    proto_opts => #{protocols => [http1, http2]}
}).

Roadrunner negotiates the protocol per connection from the ALPN offer; HTTP/1.1 clients still work on the same listener. Default when protocols is omitted is [http1] (HTTP/1.1 only).

Summary

Functions

Roadrunner serves every route and reply kind.

Drains the listener named Name, then stops it like stop/1. The listen socket closes, every connection and WebSocket session is told to wind down -- a live view runs handle_drain/2 and closes 1001, which clients reconnect from -- and the call waits up to Timeout milliseconds for them before the listener exits; sessions still open then are killed. Returns {error, not_found} if Name is not a running listener.

Formats runtime errors raised with an error_info annotation pointing at this module. Picked up by erl_error:format_exception/3.

Tells every connection and WebSocket session of the listener Name to wind down, as drain/2 does, while the listener keeps accepting. Returns {error, not_found} if Name is not a running listener.

Recompiles every Arizona-managed dispatch by walking persistent terms. Called by the dev hot reloader after a successful recompile.

Starts a roadrunner listener named Name configured with Arizona routes from Opts.

Stops the listener named Name and clears its persistent terms. Returns {error, not_found} if Name is not a running listener.

Functions

capabilities()

-spec capabilities() -> #{routes := [mcp | reload], replies := [stream | sendfile | loop]}.

Roadrunner serves every route and reply kind.

drain(Name, Timeout)

-spec drain(Name, Timeout) -> ok | {error, not_found} when Name :: atom(), Timeout :: non_neg_integer().

Drains the listener named Name, then stops it like stop/1. The listen socket closes, every connection and WebSocket session is told to wind down -- a live view runs handle_drain/2 and closes 1001, which clients reconnect from -- and the call waits up to Timeout milliseconds for them before the listener exits; sessions still open then are killed. Returns {error, not_found} if Name is not a running listener.

format_error(Reason, Stacktrace)

-spec format_error(Reason, Stacktrace) -> ErrorInfo
                      when
                          Reason :: term(), Stacktrace :: [tuple()], ErrorInfo :: #{general := iolist()}.

Formats runtime errors raised with an error_info annotation pointing at this module. Picked up by erl_error:format_exception/3.

notify_drain(Name, Timeout)

-spec notify_drain(Name, Timeout) -> ok | {error, not_found}
                      when Name :: atom(), Timeout :: non_neg_integer().

Tells every connection and WebSocket session of the listener Name to wind down, as drain/2 does, while the listener keeps accepting. Returns {error, not_found} if Name is not a running listener.

recompile_routes()

-spec recompile_routes() -> ok.

Recompiles every Arizona-managed dispatch by walking persistent terms. Called by the dev hot reloader after a successful recompile.

Refreshes two tables in lockstep:

  • {arizona_roadrunner_dispatch, Name} (read by arizona_roadrunner_req:resolve_route/3 on WS navigate), and
  • the listener's own compiled route table (read by roadrunner on every incoming HTTP request).

Both must move together — otherwise WS navigate sees new routes while a fresh HTTP request still 404s against the listener's stale table. A stashed listener that is no longer running is skipped, so it cannot stop the others from reloading.

start(Name, Opts)

-spec start(Name, Opts) -> {ok, pid()} | {error, term()} when Name :: atom(), Opts :: map().

Starts a roadrunner listener named Name configured with Arizona routes from Opts.

stop(Name)

-spec stop(Name) -> ok | {error, not_found} when Name :: atom().

Stops the listener named Name and clears its persistent terms. Returns {error, not_found} if Name is not a running listener.