6 · WebSockets

WebSockets are routes, registered the same way as HTTP handlers. app_ws(pattern, handler) uses the same {param} matcher — so a chat room is just a param.

An echo room

import "std/websocket"

fn ws_room_handler(info: Array) -> int {
    let fd: int = info[0]
    let headers: Array = info[2]
    let params: Array = info[3]   // flat [k1, v1, k2, v2, ...]

    var room: String = "?"
    var i: int = 0
    while i + 1 < params.length() {
        let k: String = params[i]
        if k == "room" {
            let v: String = params[i + 1]
            room = v
        }
        i = i + 2
    }

    let key: String = http_find_header(headers, "Sec-WebSocket-Key")
    tcp_write(fd, ws_handshake_response(key))

    let data: String = tcp_read_partial(fd, 4096)
    if data.length() > 0 and !ws_is_close(data) {
        let msg: String = ws_parse(data)
        tcp_write(fd, ws_frame("[" + room + "] " + msg))
    }
    tcp_write(fd, ws_close())
    tcp_close(fd)
    return 1   // we own the fd — the server won't touch it
}

fn main() {
    let app: App = app_new()
    app_ws("/ws/chat/{room}", ws_room_handler)
    serve_app(app, 3000, 8)
}

The handler's contract:

Try it

Connect with any WebSocket client to ws://localhost:3000/ws/chat/general and send a message — you get back [general] your message. The {room} param proves it routed.

This handler serves one message and closes — the smallest complete example. A real chat keeps the loop open and, for broadcast across connections, shares state through nyx-kv Pub/Sub. Reading and writing partial frames is all in std/websocket.