← server · version 0.7.5
42 public items, in source order. Generated from the registry index, which re-derives them from the package source at publish time — so a signature here is the one that version actually ships.
pub struct Server { handle: integer }
A listening server, as a native socket handle. listen opens one (listen_on, listen_tls and the try_ forms too), and next waits for its next request.
pub struct WebSocket { ws_id: integer }
An upgraded WebSocket connection, as a native handle. ws_upgrade makes one from the current request; next reads a message and send writes one.
pub struct Request { method: text, path: text, body: text, headers: vector<text> }
One HTTP request: its method, path and body, and its headers as Key: Value lines. next on a Server answers one, and respond or one of its variants answers it.
pub fn listen(port: integer) -> Server
Bind a TCP listener on port and return the server.
Failing to take the port is FATAL here, deliberately. The native listener answers -1 when the bind fails, and a Server built on -1 is indistinguishable from a healthy one at every call site: run spins forever on an empty event pump, so the process reports itself up and serves nobody. Worse, that state is invisible from OUTSIDE the process too — a readiness probe that connects to the port reaches whoever DID win the bind and reports success — so the failure surfaces somewhere else entirely, as a client that hangs or talks to a stranger's server.
Use try_listen when losing the port is an outcome you mean to handle: retrying on another port, or scanning a range.
pub fn try_listen(port: integer) -> Server
Like listen, but hands back an UNBOUND server instead of halting when the port cannot be taken. Check bound() before using it: every other method on an unbound server does nothing, quietly.
pub fn listen_on(addr: text, port: integer) -> Server
Bind on a chosen address rather than the wildcard 0.0.0.0 that plain listen uses. "127.0.0.1" restricts the server to loopback — unreachable from the network — which is the safe default a machine sitting behind a reverse proxy or an SSH tunnel wants, and the one listen deliberately does NOT give (it binds every interface). An empty addr means 0.0.0.0. Halts if the bind fails; try_listen_on hands back an unbound server instead.
pub fn try_listen_on(addr: text, port: integer) -> Server
listen_on without the halt: an unbound server comes back instead. Check bound() — every other method on an unbound server does nothing, quietly.
pub fn listen_tls(port: integer, cert: text, key: text) -> Server
Serve TLS (https:// / wss://). cert is a PEM certificate CHAIN (leaf first, then any intermediates — what an ACME client writes as fullchain.pem); key is the PEM private key. Both are the file CONTENTS, not paths, so the caller reads them however it likes (file(path).content()), keeping this layer free of any filesystem policy. The op surface is identical to a plaintext server — accept, respond, WebSocket upgrade all work the same, just over TLS. Halts if the certificate/key is unusable or the bind fails (never a silent fallback to cleartext — a TLS server that quietly served http would be the worst surprise).
pub fn try_listen_tls(port: integer, cert: text, key: text) -> Server
listen_tls without the halt. Check bound(); a bad certificate and a taken port are the same answer here, so log the port before deciding which it was.
pub fn try_listen_tls_on(addr: text, port: integer, cert: text, key: text) -> Server
TLS on a chosen bind address — listen_tls + listen_on combined. An empty addr means 0.0.0.0.
pub fn bound(self: Server) -> boolean
True when this server actually owns its port. Always true for a listen() result (that halts otherwise) — it is the check that makes a try_listen() result usable.
pub fn next_nonblocking(self: Server) -> Request?
Non-blocking variant of next(). Returns null immediately if no connection is pending. Use this when the server interleaves HTTP accept with multi-client WS polling on a single port (single_port audience demo / future TTT-style multi-tab apps).
pub operator next(self: Server) -> Request?
BLOCK until a client connects, and answer its request. The heart of stack 1: call it in a loop, respond exactly once, and the connection closes.
Null when the listener can no longer accept, which ends a for req in srv. next_nonblocking is the variant that answers null immediately when nobody is waiting.
pub fn respond(self: Request, status: integer, body: text)
Answer with text/plain and CLOSE the connection. Exactly one respond per request — a second is not an error, it just has no connection left to write to. respond_typed is this with a Content-Type you choose.
pub fn respond_typed(self: Request, status: integer, body: text, content_type: text)
Send an HTTP response with a caller-specified Content-Type and close the connection. Use for HTML / CSS / JSON / WASM payloads; respond defaults to text/plain.
pub fn respond_bytes(self: Request, status: integer, body: vector<u8>, content_type: text)
Send a response whose body is RAW BYTES — an image, a font, a wasm module, any payload that is not text.
⚠⚠ USE THIS FOR ANYTHING BINARY; the text responses cannot carry it and FAIL SILENTLY. respond/respond_typed take loft text, and a text built from non-UTF-8 bytes is the EMPTY text (text_from_bytes documents exactly that). So serving a PNG through them answers **200 OK with Content-Length: 0** — a success the caller cannot distinguish from an empty file, and a broken image with no error anywhere. Measured 2026-08-21: the loft review viewer had been serving every doc/images/*.png that way.
content_type empty defaults to application/octet-stream, which is the honest answer for bytes of unknown kind — name the real type when you know it (image/png, font/woff2, application/wasm).
pub fn header(self: Request, name: text) -> text
Look up a request header case-insensitively (e.g. "Range", "Origin", "If-None-Match"); returns "" if absent. Values containing ':' (an Origin URL, a date) are rejoined intact.
pub fn respond_with_headers(self: Request, status: integer, body: text, headers: vector<text>)
Send a response with caller-controlled headers (Content-Range, ETag, the CORS headers, …). headers is a vector of "Key: Value" lines; Content-Length is auto-added from the body unless a header supplies one; the body is written verbatim (binary-safe).
pub fn cors_headers(origin: text) -> vector<text>
Standard CORS headers so a browser client can read Content-Range / Content-Length / ETag on a cross-origin byte-range fetch. origin is the allowed origin ("*" for any).
pub fn serve_range(self: Request, body: text, extra: vector<text>) -> boolean
Serve a byte range of body when the request carries a Range header, adding extra headers (CORS / ETag) to the 206 or 416. Returns true if a range response was sent; false when there is no Range header (the caller serves the full body). Single-range "bytes=off-last" / open-ended "bytes=off-" only.
pub fn serve_data(self: Request, body: text, etag: text, origin: text) -> boolean
One-call helper to serve a (possibly binary) resource with full byte-range + CORS + HEAD + conditional support — the server counterpart to the web client's http_get_range / http_size. etag "" skips conditional; origin "" skips CORS.
pub fn respond_html(self: Request, body: text)
Convenience wrappers for the common cases TTT v3 needs.
pub fn respond_css(self: Request, body: text)
200 as text/css.
⚠ A stylesheet written as a loft literal needs its braces DOUBLED — { opens an interpolation, so "body {{ color: red }}" is what produces body {. Reading the file instead (file(path).content()) sidesteps it entirely.
pub fn respond_js(self: Request, body: text)
200 as application/javascript. Same brace-doubling trap as respond_css, and script bodies hit it on the first function they declare.
pub fn respond_loft(self: Request, body: text)
200, serving loft SOURCE. There is no registered MIME type for it, so this goes out as text/plain — which is what makes it read in a browser tab rather than download.
pub fn respond_404(self: Request)
The not-found answer. Worth calling on every unmatched path: a request with no respond at all leaves the client waiting for a body that never comes.
pub fn close(self: Server)
Give the port back. A server that is never closed holds its port until the process exits, which is what makes a restarted program fail to bind.
pub fn ws_upgrade() -> WebSocket?
Turn the connection the last next handed over into a WebSocket. Answers null if the request was not a valid upgrade — check before using it.
Takes no request argument because it acts on the one parked connection: this is the single-client stack, where there is only ever one.
pub operator next(self: WebSocket) -> text?
The next frame's payload, or null when the socket is done. Nullable here, which is what lets a while over it end cleanly.
Ask last_opcode() afterwards to tell a text frame from a binary one.
pub fn send(self: WebSocket, msg: text) -> boolean
Send a TEXT frame. False means the peer is gone — the return is the disconnect notice, since nothing else reports one.
pub fn send_binary(self: WebSocket, msg: text) -> boolean
Send msg as a binary WebSocket frame (opcode 2). Loft text is a byte buffer — pack u8/u16/i32/etc. into it via to_bytes style code, then ship it.
pub fn last_opcode(self: WebSocket) -> integer
Opcode of the last frame returned by next on this socket. Use after next to decide whether to interpret the payload as utf-8 text or as a binary blob.
pub fn close(self: WebSocket)
Close the socket. The peer sees a clean close rather than a dropped connection, which is the difference between a reconnect and an error.
pub struct WsEvent { connected: boolean, message: boolean, http: boolean, cid: integer, msg_id: integer, payload: text, path: text }
Event delivered to the on_event handler. connected and message are mutually-exclusive booleans for fast dispatch without an integer enum. Wire format <msg_id>:<payload> is parsed by run so msg_id and payload come pre-split.
pub fn run(self: Server, on_event: fn(WsEvent))
Block forever, draining events and calling the handler. Malformed message frames are logged at warn level and silently dropped — the handler never sees them.
pub fn poll_event(self: Server) -> WsEvent?
Non-blocking single-event drain — returns the next WsEvent, or null when nothing is pending. Lets a server that also needs a periodic tick (timers, decay, snapshots) run its OWN loop while still using the efficient Rust-side event pump: drain poll_event in a batch, do the periodic work, idle-sleep when a pass produced nothing. run above is the convenience wrapper for servers that only react to events.
Disconnect (kind 2) and malformed message frames are skipped internally (the caller never sees them), so a null return always means "idle".
pub fn respond_html(self: Server, body: text)
Reply to the HTTP request currently parked by a http WsEvent and close it. Call exactly once from the handler for an ev.http event.
pub fn respond_typed(self: Server, status: integer, body: text, content_type: text)
The Server twin of Request::respond_typed, for replying to the request parked by an http WsEvent inside a run handler.
pub fn respond_404(self: Server)
The same not-found answer, from inside a run handler.
pub fn send_to(self: Server, client_id: integer, msg: text) -> boolean
Send a frame to a specific client. Returns true on success.
pub fn broadcast(self: Server, msg: text) -> integer
Send a frame to every active client. Returns the count of successful sends. Iteration happens entirely in Rust.
pub fn disconnect(self: Server, client_id: integer)
Forcefully close one client's connection. The slot is freed for reuse by future accepts.