Skip to content

Lifecycle — WebSocket component

One sandboxed component process per connection. The host owns the socket and delivers each inbound frame to the process's mailbox; the process replies through a writer pid. See the overview for the shared two-domain model and failure vocabulary.

Shape (what you write)

rust
use rusm_rs::ws::{self, Connection, Handler};

struct Echo;
impl Handler for Echo {
    fn open(&mut self, conn: &Connection) {
        conn.send(b"welcome\n");
    }
    fn message(&mut self, conn: &Connection, data: Vec<u8>) {
        conn.send(&data); // echo this connection's frame
    }
}

#[rusm_rs::main]
fn run() {
    ws::serve(Echo);
}
ts
import { websocket } from "rusm-ts";

// One worker per connection; reply with `socket.send(…)`.
export default websocket({
  open(socket) {
    socket.send("welcome\n");
  },
  message(socket, data) {
    socket.send(data); // echo this connection's frame
  },
});

There is one handler instance per connection, so its state (Rust &mut self, or a TypeScript closure) is this connection's state — no cross-connection sharing.

Platform owns / you write

  • Platform owns: the upgrade handshake, the socket and its writer process (a Wasm-free process that owns the sink — message 1 to your process is its pid), delivering inbound frames as mailbox messages, and killing the process when the socket closes.
  • You write: open / message, replying with conn.send(…).

Lifecycle events

EventPlatform domainApplication domainResult
Normal open + framesupgrade → spawn → deliver msg 1 = writer pid → each frame as a messageopen, then message per frame, replying via conn.sendframes handled/echoed
Client disconnect (clean close)detects the close and kills the per-connection process; reclaims the writerthe process is torn down (no close callback needed)socket closed; resources reclaimed
Connection error (reset, bad frame, protocol error)the connection task ends; the process is killedthat connection gone
Crash (trap) in a handlerthe process is Crashed; the platform tears down its writer + socketthe panic! / .unwrap()that connection dropped; all others + the listener untouched
Memory crash (OOM)the StoreLimiter cap trips a trap → handled like a crashexceeded max-memory-mbthat connection dropped; the instance discarded

Notes

  • Containment by construction. Connections share nothing, so a crash or OOM is contained to one client — there is no shared instance whose failure could affect others.
  • No close callback — and none needed. The per-connection process is the connection; when the socket closes the platform kills the process, and exit cascades (links) clean up anything it owned. Shared state (rooms, presence) belongs in a service component, not in the per-connection process.
  • Establishment cost is a spawn. Each new connection is a fresh sandboxed process — the connection-storm benchmark measures exactly this (sandboxed-process-per-connection establishments per second).

Prev: SSE component · Next: Worker component

MIT licensed