brazier/transport
Types
The allowed_hosts of a listener, parsed once when it starts
(Django’s ALLOWED_HOSTS, SEC-16).
pub opaque type AllowedHosts
The ws_allowed_origins of a listener, parsed once when it starts.
pub opaque type AllowedOrigins
Messages sent from the client (browser) to the server. This is Brazier’s wire protocol — not Lustre’s internal format.
pub type ClientMessage {
ClientEvent(
name: String,
handler_id: String,
data: String,
target_path: String,
clock: Int,
ops: List(ClientOp),
)
ClientHeartbeat
ClientJoin(token: String, path: String)
ClientNavigate(path: String, clock: Int)
ClientRequestSync
ClientEventBatch(events: List(ClientMessage))
}
Constructors
-
ClientEvent( name: String, handler_id: String, data: String, target_path: String, clock: Int, ops: List(ClientOp), )A DOM event was fired (click, input, submit, etc).
Arguments
- name
-
The event name (e.g. “click”, “input”).
- handler_id
-
The handler identifier from the data-brazier-event-* attribute value. This is the semantic identifier set by the view (e.g. “increment”, “decrement”).
- data
-
JSON-encoded event data.
- target_path
-
Path to the element in the VDOM tree that fired the event.
- clock
-
Monotonic event clock value for ordering and acknowledgment. Reference: LiveView 1.1 event clocking.
- ops
-
Model operations from a client-side model diff: the frame’s
ops, a JSON array embedded raw ("ops":[...]), each op checked byclient_op_decoder.[]when the frame has noops. The server treats them as hints only and never applies them: the server’supdateis authoritative. A frame whoseopsis not such an array is refused (errorframe"Invalid event ops"). The size is bounded bymax_message_bytes.
-
ClientHeartbeatClient sends a heartbeat to keep the connection alive.
-
ClientJoin(token: String, path: String)Client is requesting initial state after connecting. Includes an optional session token for state recovery and the current URL path for route-aware apps.
-
ClientNavigate(path: String, clock: Int)Client navigated to a new URL path (SPA navigation).
clockis stamped from the same counter as the events’ clocks, and the frame that carries the navigation’s state acknowledges it (ack_clock), so the client knows which render shows the new page (it scrolls to the URL’s fragment then). Absent on the wire is 0, as for an event. -
ClientRequestSyncClient requests a full authoritative model sync without remounting HTML.
-
ClientEventBatch(events: List(ClientMessage))Batch of events: LOCAL events replayed + MODEL event at the end. Sent when a MODEL event fires after LOCAL events accumulated state.
One model operation a client attached to an event: an RFC 6902 op
(add, remove, replace, move, copy, test) or Brazier’s
append (see brazier/patch). path and from are JSON Pointers
(RFC 6901) that pass through no unsafe key (patch.unsafe_keys).
from is set for move and copy only, value for add, replace,
test and append only (an array for append). Members RFC 6902 does
not define for an op are ignored, as the RFC says.
pub type ClientOp {
ClientOp(
op: String,
path: String,
from: option.Option(String),
value: option.Option(dynamic.Dynamic),
)
}
Constructors
-
ClientOp( op: String, path: String, from: option.Option(String), value: option.Option(dynamic.Dynamic), )
Unique identifier for a WebSocket connection. Used for logging and session tracking.
pub type ConnectionId =
String
State held by each WebSocket connection process.
pub type ConnectionState {
ConnectionState(
id: String,
socket: server.Socket,
on_event: fn(String, ClientMessage) -> Nil,
on_close: fn(String) -> Nil,
event_count: Int,
rate_window_start: Int,
security_limits: SecurityLimits,
heartbeat_count: Int,
heartbeat_window_start: Int,
buffer: BitArray,
reassembly: @internal Reassembly,
peer_ip: String,
request: request.Request(server.Connection),
ws_auth: option.Option(
fn(request.Request(server.Connection)) -> Result(
Nil,
String,
),
),
middlewares: List(
fn(
request.Request(server.Connection),
fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
),
) -> response.Response(server.ResponseBody),
),
page: LivePage,
last_activity_ms: Int,
ping_seq: Int,
pending_ping: option.Option(Int),
decode_errors: Int,
rate_violations: Int,
timer_subject: process.Subject(WsTimer),
ping_timer: option.Option(process.Timer),
pong_timer: option.Option(process.Timer),
idle_timer: option.Option(process.Timer),
auth_timer: option.Option(process.Timer),
opened_at: Int,
frames_in: Int,
frames_out: Int,
bytes_in: Int,
bytes_out: Int,
warned_no_build: Bool,
)
}
Constructors
-
ConnectionState( id: String, socket: server.Socket, on_event: fn(String, ClientMessage) -> Nil, on_close: fn(String) -> Nil, event_count: Int, rate_window_start: Int, security_limits: SecurityLimits, heartbeat_count: Int, heartbeat_window_start: Int, buffer: BitArray, reassembly: @internal Reassembly, peer_ip: String, request: request.Request(server.Connection), ws_auth: option.Option( fn(request.Request(server.Connection)) -> Result(Nil, String), ), middlewares: List( fn( request.Request(server.Connection), fn(request.Request(server.Connection)) -> response.Response( server.ResponseBody, ), ) -> response.Response(server.ResponseBody), ), page: LivePage, last_activity_ms: Int, ping_seq: Int, pending_ping: option.Option(Int), decode_errors: Int, rate_violations: Int, timer_subject: process.Subject(WsTimer), ping_timer: option.Option(process.Timer), pong_timer: option.Option(process.Timer), idle_timer: option.Option(process.Timer), auth_timer: option.Option(process.Timer), opened_at: Int, frames_in: Int, frames_out: Int, bytes_in: Int, bytes_out: Int, warned_no_build: Bool, )Arguments
- id
-
Unique ID for this connection, used in logging.
- socket
-
The gen_tcp socket for this connection.
- on_event
-
Callback invoked when a client event is received (per-connection).
- on_close
-
Callback invoked when this connection closes (per-connection).
- event_count
-
Rate limiting: count of events in current 1-second window.
- rate_window_start
-
Rate limiting: start of current window (monotonic milliseconds).
- security_limits
-
Security limits for this connection (copied from TransportConfig at init).
- heartbeat_count
-
Heartbeat rate limiting: count in current 1-second window.
- heartbeat_window_start
-
Heartbeat rate limiting: start of current window (monotonic milliseconds).
- buffer
-
Receive buffer holding at most one partial frame between reads.
- reassembly
-
Fragmented message reassembly state.
- peer_ip
-
Peer IP address, for logs.
- request
-
The original upgrade request (for periodic
ws_authre-checks). - ws_auth
-
The configured
ws_auth, re-run everyws_auth_recheck_interval_ms. - middlewares
-
The listener’s middleware pipeline: the gate every navigation and join of this connection must pass (
gate_live_page). - page
-
The page this connection last passed the middleware gate for: the upgrade’s page, then each admitted navigation.
- last_activity_ms
-
Monotonic ms of the last application message from the client.
- ping_seq
-
Sequence number of the last ping sent.
- pending_ping
-
Sequence number of the ping awaiting a pong, if any.
- decode_errors
-
Undecodable messages received so far.
- rate_violations
-
Rate limit violations in the current rate window.
- timer_subject
-
Subject the connection’s own timers deliver to.
- ping_timer
-
Next server ping.
- pong_timer
-
Deadline for the outstanding pong.
- idle_timer
-
Next idle check.
- auth_timer
-
Next
ws_authre-check (only whenws_authis configured). - opened_at
-
Native monotonic time the connection started (after the 101).
- frames_in
-
WebSocket frames decoded from the client.
- frames_out
-
WebSocket frames written to the client (including the close frame).
- bytes_in
-
TCP bytes received from the client after the upgrade.
- bytes_out
-
Bytes written to the client after the upgrade.
- warned_no_build
-
Whether a join without a build id was already warned about on this connection (
check_build): later ones are logged at debug.
Internal messages that the connection process can receive. These come from the runtime (pushing patches) or from TCP events.
pub type InternalMessage {
SendMount(payload: String)
SendError(reason: String)
SendModelSync(
model_json: String,
version: Int,
ack_clock: Int,
streams_json: String,
)
SendPatch(
ops_json: String,
version: Int,
ack_clock: Int,
streams_json: String,
)
SendNavigate(path: String)
SendReplaceNavigate(path: String)
SendHardNavigate(path: String)
SendTitle(title: String)
CloseForBackpressure(reason: String)
ReceivedTcpData(data: BitArray)
TcpConnectionClosed
TcpConnectionError(reason: String)
}
Constructors
-
SendMount(payload: String)A mount payload needs to be sent to the client.
-
SendError(reason: String)An error needs to be sent to the client.
-
SendModelSync( model_json: String, version: Int, ack_clock: Int, streams_json: String, )Send authoritative Model state to the client.
model_jsonmust be one valid JSON value andstreams_jsonone valid JSON array; seeServerModelSync. -
SendPatch( ops_json: String, version: Int, ack_clock: Int, streams_json: String, )Send incremental model patch to the client.
ops_jsonandstreams_jsonmust each be one valid JSON array; seeServerPatch. -
SendNavigate(path: String)Send navigation redirect to the client (pushState).
-
SendReplaceNavigate(path: String)Send navigation to the client that replaces the history entry.
-
SendHardNavigate(path: String)Send hard navigation to the client (full page reload).
-
SendTitle(title: String)Send the document title to the client.
-
CloseForBackpressure(reason: String)Close the connection because the server has detected sustained pressure.
-
ReceivedTcpData(data: BitArray)Raw TCP data received — needs WebSocket frame decoding.
-
TcpConnectionClosedTCP connection closed by peer.
-
TcpConnectionError(reason: String)TCP error occurred.
The page a live connection mounts or navigates to: a same-origin path and its query.
pub type LivePage {
LivePage(path: String, query: option.Option(String))
}
Constructors
-
LivePage(path: String, query: option.Option(String))
Configurable security limits for the transport layer.
All fields have sensible defaults via default_security_limits().
validate_security_limits rejects nonsensical values; the listener
refuses to start with invalid limits.
pub type SecurityLimits {
SecurityLimits(
max_message_bytes: Int,
max_events_per_second: Int,
max_connections: Int,
max_http_headers: Int,
max_http_header_bytes: Int,
pre_upgrade_timeout_ms: Int,
max_connections_per_ip: Int,
max_request_line_bytes: Int,
max_body_bytes: Int,
body_read_timeout_ms: Int,
request_timeout_ms: Int,
keepalive_idle_timeout_ms: Int,
max_keepalive_requests: Int,
send_timeout_ms: Int,
drain_timeout_ms: Int,
trusted_proxies: List(String),
forwarding_header: ip.ForwardingHeader,
max_ws_connections_per_ip: Int,
ws_ping_interval_ms: Int,
ws_pong_timeout_ms: Int,
ws_idle_timeout_ms: Int,
ws_send_timeout_ms: Int,
ws_max_outbound_queue: Int,
ws_auth_recheck_interval_ms: Int,
ws_max_decode_errors: Int,
ws_max_rate_violations: Int,
ws_allowed_origins: List(String),
allowed_hosts: List(String),
stream_max_limit: Int,
stream_item_max_bytes: Int,
stream_ops_max_per_update: Int,
stream_frame_max_bytes: Int,
stream_key_max_bytes: Int,
)
}
Constructors
-
SecurityLimits( max_message_bytes: Int, max_events_per_second: Int, max_connections: Int, max_http_headers: Int, max_http_header_bytes: Int, pre_upgrade_timeout_ms: Int, max_connections_per_ip: Int, max_request_line_bytes: Int, max_body_bytes: Int, body_read_timeout_ms: Int, request_timeout_ms: Int, keepalive_idle_timeout_ms: Int, max_keepalive_requests: Int, send_timeout_ms: Int, drain_timeout_ms: Int, trusted_proxies: List(String), forwarding_header: ip.ForwardingHeader, max_ws_connections_per_ip: Int, ws_ping_interval_ms: Int, ws_pong_timeout_ms: Int, ws_idle_timeout_ms: Int, ws_send_timeout_ms: Int, ws_max_outbound_queue: Int, ws_auth_recheck_interval_ms: Int, ws_max_decode_errors: Int, ws_max_rate_violations: Int, ws_allowed_origins: List(String), allowed_hosts: List(String), stream_max_limit: Int, stream_item_max_bytes: Int, stream_ops_max_per_update: Int, stream_frame_max_bytes: Int, stream_key_max_bytes: Int, )Arguments
- max_message_bytes
-
Maximum WebSocket message size in bytes. Default: 65536 (64KB). Bounds both a single frame (checked from the frame header, before the payload is buffered) and a reassembled fragmented message. Exceeding it closes the connection with 1009.
- max_events_per_second
-
Maximum events per second per connection. Default: 50. Every event inside an
event_batchcounts individually. - max_connections
-
Maximum concurrent HTTP/WebSocket connections globally. Default: 10000.
- max_http_headers
-
Maximum number of HTTP headers accepted before upgrade. Default: 100.
- max_http_header_bytes
-
Maximum total HTTP header bytes accepted before upgrade. Default: 65536.
- pre_upgrade_timeout_ms
-
Timeout for request line/header reads before upgrade, in milliseconds. Default: 5000.
- max_connections_per_ip
-
Maximum concurrent connections from one peer IP. Peers listed in
trusted_proxiesare exempt (they multiplex many clients). Default: 256. - max_request_line_bytes
-
Maximum request-line length in bytes; longer lines get 414. Default: 8192.
- max_body_bytes
-
Maximum declared request body size in bytes; larger Content-Length gets 413 before the handler runs. Default: 8388608 (8MB).
- body_read_timeout_ms
-
Maximum wait for each chunk of request body, in milliseconds. Default: 30000.
- request_timeout_ms
-
Absolute deadline for reading a request body, in milliseconds from the moment its head was parsed (the head itself is bounded by
pre_upgrade_timeout_ms). Default: 60000. - keepalive_idle_timeout_ms
-
How long an idle keep-alive connection waits for the next request, in milliseconds. Default: 15000.
- max_keepalive_requests
-
Maximum requests served on one keep-alive connection. Default: 1000.
- send_timeout_ms
-
Maximum time a blocked socket send may take before the connection is closed, in milliseconds. Default: 30000.
- drain_timeout_ms
-
How long a stopping listener waits for its open connections to finish, in milliseconds. When the listener stops, an idle keep-alive connection closes at once, a request in progress is answered with
connection: close, and a WebSocket is closed with 1001. A connection still open after this long is killed (and logged as an error).0kills every connection at once. The environment variableBRAZIER_SHUTDOWN_TIMEOUToverrides it for an application. Default: 5000. - trusted_proxies
-
Reverse proxies (IP addresses or CIDRs) whose
forwarding_headeris believed. Parsed once, when the listener starts. Empty means the socket peer address is always the client address. Default: []. - forwarding_header
-
The one header the
trusted_proxiesset to name the client and its scheme:ip.Forwarded(for=andproto=),ip.XForwardedFor(withX-Forwarded-Proto) orip.NoForwardingHeader(the peer, overhttp). The other header is never read. A header with no trusted proxies is refused. Default:ip.NoForwardingHeader. - max_ws_connections_per_ip
-
Maximum concurrent WebSocket connections per peer IP (per listener). Upgrades beyond it get 429. Default: 100. Behind a reverse proxy every client shares the proxy’s address — raise this or enforce it upstream.
- ws_ping_interval_ms
-
Interval between server pings, in milliseconds. Default: 30000.
- ws_pong_timeout_ms
-
Time a client has to answer a ping before the connection is closed (1000, “pong timeout”), in milliseconds. Default: 20000.
- ws_idle_timeout_ms
-
Close the connection (1000, “idle timeout”) when no application message — including the client heartbeat, sent every 30s — arrives for this long, in milliseconds. Default: 90000.
- ws_send_timeout_ms
-
A socket send that cannot complete within this many milliseconds fails and the socket is closed (slow reader protection). Default: 15000.
- ws_max_outbound_queue
-
Maximum messages queued for the connection process. When the runtime produces faster than the client reads, the connection closes with 1013. Default: 1000.
- ws_auth_recheck_interval_ms
-
Interval for re-running
ws_authon a live connection, in milliseconds. A failed re-check closes with 4401. Default: 60000. - ws_max_decode_errors
-
Undecodable messages tolerated per connection before it is closed with 1008. Default: 10.
- ws_max_rate_violations
-
Rate limit violations tolerated within one rate window before the connection is closed with 1008. Each rejected message is one violation, so the default tolerates a burst of roughly three times
max_events_per_secondin one window. Default: 100. - ws_allowed_origins
-
Extra origins (e.g. “https://app.example.com”) allowed to open WebSockets in addition to the request’s own scheme/host/port. Default: [].
- allowed_hosts
-
Host names this server answers to; every HTTP request and WebSocket upgrade whose Host header names another host gets 400 (DNS rebinding protection, Django’s
ALLOWED_HOSTS). The port is not compared. An entry is a host ("example.com","127.0.0.1","[::1]"), a domain and all its subdomains (".example.com"), or"*"for every host (which turns the check off, and is logged as a warning). Empty means[".localhost", "127.0.0.1", "[::1]"]in development; in production (BRAZIER_ENV=production) the listener refuses to start with an empty list. Default: []. - stream_max_limit
-
The largest limit a stream may be used with. An op with a larger one is refused (
limit_out_of_range). At most 1000, the largest limit the wire and the client accept. Default: 1000. - stream_item_max_bytes
-
The largest encoded stream item, in bytes. An op with a larger item is refused (
item_too_large). Default: 65536. - stream_ops_max_per_update
-
The most stream ops one update may carry, all streams together; a reset counts one per item. An update with more has every stream op refused (
too_many_ops); its model part is still sent. Default: 1000. - stream_frame_max_bytes
-
The largest
streamsarray of one frame, in bytes. An update whose entries are larger has every stream op refused (frame_too_large). Default: 1048576 (1MB). - stream_key_max_bytes
-
The longest stream key, in bytes. An op with a longer key is refused (
key_too_large). Default: 256.
Messages sent from the server to the client (browser).
pub type ServerMessage {
ServerMount(payload: String)
ServerHeartbeatAck
ServerError(reason: String)
ServerModelSync(
model_json: String,
version: Int,
ack_clock: Int,
streams_json: String,
)
ServerPatch(
ops_json: String,
version: Int,
ack_clock: Int,
streams_json: String,
)
ServerNavigate(path: String)
ServerReplaceNavigate(path: String)
ServerHardNavigate(path: String)
ServerReload
ServerTitle(title: String)
}
Constructors
-
ServerMount(payload: String)Initial mount: SSR HTML for first paint.
-
ServerHeartbeatAckServer acknowledges a heartbeat.
-
ServerError(reason: String)Server-initiated error message.
-
ServerModelSync( model_json: String, version: Int, ack_clock: Int, streams_json: String, )Authoritative Model state from server. Client renders the view locally from this state. Invariant:
model_jsonis one complete, valid JSON value. It is embedded raw in the frame ("model":{...}), so invalid text would corrupt the whole frame. The runtime only sends text it decoded into apatch.Snapshot.streams_jsonis the frame’sstreamsarray (docs/design/STREAMS.md 6.1), embedded raw the same way; the runtime only sendsstream_window.encode_deltasoutput or the literal[]. -
ServerPatch( ops_json: String, version: Int, ack_clock: Int, streams_json: String, )Incremental model patch — only changed fields. Client applies ops to its cached model JSON instead of replacing entirely. Invariant:
ops_jsonis one valid JSON array of RFC 6902 operations, embedded raw in the frame ("ops":[...]). The runtime only sends the output ofpatch.diff_snapshotsor the literal[].streams_jsonis as onServerModelSync. -
ServerNavigate(path: String)Server-initiated navigation (redirect via pushState).
-
ServerReplaceNavigate(path: String)Server-initiated navigation that replaces the current history entry (
replaceState). Encoded asnavigatewith"replace": true. -
ServerHardNavigate(path: String)Server-initiated hard navigation (full page reload via window.location.href). Use when the browser needs to make a real HTTP request (e.g., to receive Set-Cookie).
-
ServerReloadDev mode: tell browser to reload.
-
ServerTitle(title: String)The document title for the session’s state: sent on every join and whenever an update changes it. The client sets
document.title.
Why a per-connection runtime did not start. Either way the socket is closed right after the upgrade, before any other frame. The factory logs the cause where it happens (Brazier’s runtime does, with the connection id); the transport logs the close at info.
pub type StartupError {
InitRefused(error: error.InitError)
StartupFailed(reason: String)
}
Constructors
-
InitRefused(error: error.InitError)The app’s
initrefused the session. The socket closes witherror.init_close_code(4000 + the status) and the reason"init refused"; the app’s own reason is only logged. -
StartupFailed(reason: String)Anything else: an invalid page URL, a crash in
init, a refused presence track. The socket closes with 1011"startup failure".
Configuration for the transport layer.
pub type TransportConfig {
TransportConfig(
port: Int,
on_connect: fn(String, process.Subject(InternalMessage)) -> Nil,
on_event: fn(String, ClientMessage) -> Nil,
on_disconnect: fn(String) -> Nil,
page_html: option.Option(@internal RenderedPage),
middlewares: List(
fn(
request.Request(server.Connection),
fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
),
) -> response.Response(server.ResponseBody),
),
static_config: option.Option(static.StaticConfig),
runtime_factory: option.Option(
fn(
String,
process.Subject(InternalMessage),
request.Request(server.Connection),
) -> Result(
#(fn(String, ClientMessage) -> Nil, fn(String) -> Nil),
StartupError,
),
),
ws_auth: option.Option(
fn(request.Request(server.Connection)) -> Result(
Nil,
String,
),
),
ssr_factory: option.Option(
fn(request.Request(server.Connection), String) -> @internal RenderedPage,
),
security_limits: SecurityLimits,
api_handler: option.Option(
fn(request.Request(server.Connection)) -> option.Option(
response.Response(server.ResponseBody),
),
),
)
}
Constructors
-
TransportConfig( port: Int, on_connect: fn(String, process.Subject(InternalMessage)) -> Nil, on_event: fn(String, ClientMessage) -> Nil, on_disconnect: fn(String) -> Nil, page_html: option.Option(@internal RenderedPage), middlewares: List( fn( request.Request(server.Connection), fn(request.Request(server.Connection)) -> response.Response( server.ResponseBody, ), ) -> response.Response(server.ResponseBody), ), static_config: option.Option(static.StaticConfig), runtime_factory: option.Option( fn( String, process.Subject(InternalMessage), request.Request(server.Connection), ) -> Result( #(fn(String, ClientMessage) -> Nil, fn(String) -> Nil), StartupError, ), ), ws_auth: option.Option( fn(request.Request(server.Connection)) -> Result(Nil, String), ), ssr_factory: option.Option( fn(request.Request(server.Connection), String) -> @internal RenderedPage, ), security_limits: SecurityLimits, api_handler: option.Option( fn(request.Request(server.Connection)) -> option.Option( response.Response(server.ResponseBody), ), ), )Arguments
- port
-
Port to listen on.
- on_connect
-
Callback invoked when a new WebSocket connection is established.
- on_event
-
Callback invoked when a client sends an event.
- on_disconnect
-
Callback invoked when a WebSocket connection closes.
- page_html
-
Optional: pre-rendered page for SSR. If provided, HTTP requests get this instead of the default page.
- middlewares
-
Optional: middleware pipeline applied to all HTTP requests. Middleware runs before routing (SSR, static files). A WebSocket is gated on the page it mounts instead: the upgrade, every navigation and every join run the pipeline on a GET of that page with the upgrade’s headers (
gate_live_page), never on/wsitself. - static_config
-
Optional: static file serving configuration.
- runtime_factory
-
Optional: factory that creates a per-connection runtime. If set, each WebSocket connection gets its OWN runtime (like LiveView). The factory returns callbacks for this specific connection’s runtime. If None, uses the shared on_connect/on_event/on_disconnect callbacks. The factory receives the upgrade request, from which the runtime builds the connected mount’s Context (cookies, page URL).
- ws_auth
-
Optional: WebSocket authentication function. If set, runs before upgrade — returns Ok to allow, Error to reject with 401.
- ssr_factory
-
Optional: SSR factory for route-aware server-side rendering. Given the HTTP request and path, returns the rendered page for that route. The request is available for reading cookies, headers, etc. When set, HTTP requests use this instead of page_html.
- security_limits
-
Configurable security limits (message size, rate limiting, max connections). Defaults to
default_security_limits(). - api_handler
-
Optional: API route handler — runs BEFORE SSR/static file routing. If it returns Some(response), that response is sent immediately. If it returns None, the request falls through to normal routing.
Timer events a WebSocket connection process schedules for itself.
pub type WsTimer {
PingTick
PongDeadline(seq: Int)
IdleCheck
AuthRecheck
}
Constructors
-
PingTickSend the next server ping.
-
PongDeadline(seq: Int)Deadline for the pong answering ping
seq. -
IdleCheckCheck whether the connection has been idle too long.
-
AuthRecheckRe-run
ws_authagainst the original upgrade request.
Values
pub fn connection_tracker_init() -> Nil
pub fn create_handler(
config: TransportConfig,
) -> fn(request.Request(server.Connection)) -> response.Response(
server.ResponseBody,
)
Create the HTTP handler for non-WebSocket requests. Handles: API routes, static files, client JS, health check, SSR pages. API handler runs first — if it returns Some(response), that’s used. Otherwise falls through to static files, brazier_client.js, health, SSR.
pub fn decode_client_message(
raw: String,
) -> Result(ClientMessage, error.BrazierError)
Decode a JSON string from the client into a ClientMessage.
pub fn default_security_limits() -> SecurityLimits
Sensible default security limits.
pub const development_allowed_hosts: List(String)
The hosts a development server answers to when allowed_hosts is
empty: Django’s development default.
pub fn encode_server_message(msg: ServerMessage) -> String
Encode a ServerMessage to the JSON text sent over the wire.
pub fn encode_server_message_tree(
msg: ServerMessage,
) -> string_tree.StringTree
Encode a ServerMessage to the JSON text sent over the wire, as a string tree the socket writes without flattening it into one binary.
The model of a model_sync, the ops of a patch and the streams of
both are already JSON text, and they are embedded raw: "model":{...},
"ops":[...] and "streams":[...], not re-encoded as JSON strings.
That relies on the invariant documented on ServerModelSync and
ServerPatch: the text is one valid JSON value. Field order is fixed
(type, model or ops, version, ack_clock, streams).
Reference: Phoenix.Socket.V2.JSONSerializer, whose encode! returns
iodata with the already-encoded payload spliced in; LiveView sends its
rendered diff as a JSON object inside the reply, never as a string.
pub fn get_upgrade_page(
req: request.Request(server.Connection),
) -> Result(LivePage, String)
The page a WebSocket upgrade request mounts: the path and query in its
path query parameter (/ws?path=%2Fusers%2F7%3Ftab%3Dposts), which the
client sets to its own location.pathname + location.search. The first
path parameter counts. It is refused when it is missing, is not a
same-origin path (internal/local_path, the rule router.push uses) or
does not parse.
The transport gates the upgrade on this page and the runtime mounts it, so both read the same page.
pub fn origin_allowed(
origin: String,
request_scheme: String,
request_host: String,
allowed_origins: AllowedOrigins,
) -> Result(Nil, String)
Validate an Origin header against the request’s own origin (its scheme and Host header, default ports applied) and an explicit allow-list of origins. Scheme, host and port must all match.
pub fn origin_allowed_for_host(
origin: String,
request_host: String,
) -> Result(Nil, String)
Validate that an Origin header value belongs to the request Host over plain http. Empty origins, empty hosts, scheme-less values and scheme/host/port mismatches reject.
pub const page_route: String
Telemetry route of every page request. Pages are routed by the app’s typed router, which has no patterns, so all pages share one label.
pub fn parse_allowed_origins(
entries: List(String),
) -> Result(AllowedOrigins, String)
Parse ws_allowed_origins entries; each must be a bare
scheme://host[:port] origin (http or https, no path). A refusal is
returned for the caller to log once (“Invalid security limits: …”).
pub fn start(
config: TransportConfig,
) -> Result(process.Pid, error.BrazierError)
Start the transport layer — binds to the given port and begins
accepting connections. Returns the listener supervisor’s Pid.
Use start_listener to also learn the bound port (for port 0).
pub fn start_listener(
config: TransportConfig,
) -> Result(server.Listener, error.BrazierError)
Start the transport layer and return the running listener, which
reports the bound port (server.get_port) and live connection counts.
The listener supervisor is linked to the caller.
pub fn startup_error_to_string(err: StartupError) -> String
pub fn stream_limits(limits: SecurityLimits) -> @internal Limits
The stream bounds of limits, for the runtime and the dead render.
pub const stream_wire_max_limit: Int
The largest stream limit the wire allows (docs/design/STREAMS.md 6.1): the client faults on a larger one.
pub fn supervised(
config: TransportConfig,
) -> Result(
supervision.ChildSpecification(server.Listener),
error.BrazierError,
)
A child specification that starts the listener under a supervisor (the application’s root supervision tree). The configuration is checked when the specification is built, so a bad config fails before any process starts. A restarted listener binds the port of its first start again (with port 0, the port the OS assigned then).
pub fn to_listener_options(
config: TransportConfig,
) -> server.ListenerOptions
The listener options implied by a transport configuration.
pub fn trusted_proxies(
limits: SecurityLimits,
) -> Result(ip.TrustedProxies, String)
Parse the trusted_proxies and forwarding_header of limits, for
middleware.rate_limit_behind_proxies. Call it once, when building the
middleware: it parses the list on every call. The listener parses its
own copy once when it starts and refuses to start on a bad entry.
validate_security_limits rejects the same entries, so an error here
means the limits were never validated; it is logged.
pub fn validate_drain_timeout(ms: Int) -> Result(Nil, String)
A drain timeout is a receive timeout: 0 (kill at once) up to the largest timer. A refusal is returned, not logged: each caller logs it once with its own context (“Invalid security limits: …”, or the application’s “BRAZIER_SHUTDOWN_TIMEOUT: …”), so one refusal makes one log line.
pub fn validate_security_limits(
limits: SecurityLimits,
) -> Result(Nil, String)
Validate security limits. Every count, size and interval must be
positive (the two abuse thresholds may be 0, meaning “first offence
closes”, max_body_bytes may be 0, meaning “no request bodies”, and
drain_timeout_ms may be 0, meaning “kill connections at once”),
timer intervals must fit erlang:send_after/3, every allowed origin
must be a bare scheme://host[:port] origin, every allowed host a host
name, .domain or *, and in production (BRAZIER_ENV=production)
allowed_hosts must not be empty. The listener runs the same checks
when it starts.