Skip to main content

WebSocket Module Reference

STABLE(Full-duplex WebSocket client, server, framing, and TLS WSS support)

The WebSocket standard library module enables full-duplex communication over single TCP connections for real-time applications, chat protocols, financial telemetry, and live status feeds. It implements RFC 6455 framing, the HTTP Upgrade handshake, and optional TLS (wss://) via the TLS layer.

┌──────────────────────────────────────────────────────────────┐
│ WebSocket Module │
├──────────────────────────────────────────────────────────────┤
│ Client → WebSocket.connect(url, config) → Client │
│ Server → WebSocket.server(opts) → Server │
│ Handshake → HTTP Upgrade + Sec-WebSocket-Key/Accept │
│ Framing → text / binary / ping / pong / close │
│ TLS → wss:// via TLS module integration │
└──────────────────────────────────────────────────────────────┘

1. Importing the Module

import WebSocket;
import { WebSocket } from "std:WebSocket";

All WebSocket handles are RAII — dropping a client/server closes the underlying TCP socket deterministically.


2. Architecture — Handshake & Framing

Handshake Flow (RFC 6455)

Client Server
│ GET /chat HTTP/1.1 │
│ Upgrade: websocket │
│ Connection: Upgrade │
│ Sec-WebSocket-Key: dGhlIHNh..│
│ Sec-WebSocket-Version: 13 │
├──────────────────────────────►│
│ │ validate key
│ │ compute Accept = base64(sha1(key + GUID))
│ HTTP/1.1 101 Switching │
│ Upgrade: websocket │
│ Connection: Upgrade │
│ Sec-WebSocket-Accept: s3pPL..│
│◄──────────────────────────────┤
│ │
│ ◄══════ WebSocket frames ══════► (full-duplex)

The client generates a random 16-byte Sec-WebSocket-Key (base64); the server replies with Sec-WebSocket-Accept = base64(SHA1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")).

Frame Format

0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+-------------------------------+
| Masking-key (if MASK set) |
+-------------------------------+-------------------------------+
| Payload Data (masked if client→server) |
+-------------------------------------------------------------+
FieldWidthMeaning
FIN1 bit1 = final fragment, 0 = more fragments follow
RSV1-33 bitsreserved (extensions); must be 0 if no extension
Opcode4 bits0x0 cont, 0x1 text, 0x2 binary, 0x8 close, 0x9 ping, 0xA pong
MASK1 bit1 = payload masked (client→server must mask)
Payload len7/23/71 bits0-125 inline, 126 → next 16 bits, 127 → next 64 bits
Masking key0/32 bitspresent iff MASK=1
Payloadvariablemasked with payload[i] ^= mask[i%4] if masked

3. Basic Client Connection

Connecting to a remote WebSocket server:

let config = WebSocket.ClientConfig({
connect_timeout: 5000, // ms to establish TCP + handshake
idle_timeout: 30000, // ms before idle close
max_message_size: 1_000_000, // bytes
headers: { "Authorization": "Bearer token123" }
});

let client = WebSocket.connect("ws://127.0.0.1:8099", config);

if (WebSocket.isError(client)) {
print("Connection failed:", client.message);
} else {
print("Connected! State:", client.getState().toString());

// Send text frame
client.sendText("Hello from AdeshLang!");

// Send binary frame
client.sendBinary([0x01, 0x02, 0x03]);

// Receive message (blocks until frame arrives or timeout)
let msg = client.receive();
if (msg.type == "text") {
print("Received payload:", msg.payload);
} else if (msg.type == "binary") {
print("Binary bytes:", msg.payload.len());
} else if (msg.type == "close") {
print("Server closed:", msg.code, msg.reason);
}

// Clean close handshake (code 1000 = normal closure)
client.close(1000, "Client shutdown");
}

Client API

Method / FunctionSignatureDescription
WebSocket.connect(url, config?)fn connect(url: string, config?: ClientConfig): Client | ErrorConnect to ws:// or wss:// URL; performs TCP connect + handshake.
WebSocket.isError(val)fn isError(val: any): boolCheck if connect returned an error.
client.getState()fn getState(): StateConnecting, Open, Closing, Closed.
client.sendText(text)fn sendText(text: string): Result<void, WSError>Send UTF-8 text frame (FIN=1).
client.sendBinary(bytes)fn sendBinary(bytes: u8[]): Result<void, WSError>Send binary frame.
client.sendPing(data?)fn sendPing(data?: u8[]): Result<void, WSError>Send ping control frame (keepalive).
client.sendPong(data)fn sendPong(data: u8[]): Result<void, WSError>Respond to ping.
client.receive()fn receive(): MessageBlock until next message frame arrives.
client.receiveTimeout(ms)fn receiveTimeout(ms: u64): Message | nullReceive with timeout; null on timeout.
client.close(code?, reason?)fn close(code?: u16, reason?: string)Send close frame and await peer close.
client.isConnected()fn isConnected(): booltrue if state is Open.

ClientConfig

FieldTypeDefaultDescription
connect_timeoutu64 (ms)5000TCP + handshake deadline.
idle_timeoutu64 (ms)30000Close if no frames for this long.
max_message_sizeusize1_000_000Maximum reassembled message bytes.
headersMap<string,string>{}Extra HTTP headers for handshake (auth, etc.).
tls_configTLS.ConfigTLS options for wss:// (cert verification, ALPN).

4. WebSocket Server Implementation

Basic Echo Server

let server = WebSocket.server({ port: 8099, bind_address: "127.0.0.1" });

// Enable automatic echo protocol for incoming frames
server.startEcho();

print("WebSocket Echo Server listening on ws://127.0.0.1:8099");

Custom Handler Server

let server = WebSocket.server({
port: 8099,
bind_address: "127.0.0.1",
max_connections: 100,
max_message_size: 2_000_000
});

server.onConnection(fn(conn) {
print("New client:", conn.remoteAddr);

// Per-connection message loop
while (conn.isConnected()) {
let msg = conn.receive();
if (msg.type == "text") {
print("Text:", msg.payload);
conn.sendText(f"Echo: {msg.payload}");
} else if (msg.type == "binary") {
conn.sendBinary(msg.payload);
} else if (msg.type == "ping") {
conn.sendPong(msg.payload);
} else if (msg.type == "close") {
print("Client requested close:", msg.code);
break;
}
}
conn.close(1000, "Bye");
});

server.listen(); // blocks — accept loop

Room / Broadcast Server

let server = WebSocket.server({ port: 8099, bind_address: "127.0.0.1" });
let rooms = Arc::new(Mutex::new(HashMap::new()));

server.onConnection(fn(conn) {
let roomId = conn.path; // e.g., "/room/lobby" from ws://host/room/lobby
{
let mut r = rooms.lock().unwrap();
r.get(roomId).unwrapOrInsert(Vec::new()).push(conn.id);
}
// broadcast to room
server.broadcast(roomId, f"User {conn.id} joined");
});

server.listen();

Server API

MethodSignatureDescription
WebSocket.server(opts)fn server(opts: ServerOpts): ServerCreate server (does not bind until listen/startEcho).
server.startEcho()fn startEcho()Start server that echoes every frame back to sender.
server.onConnection(handler)fn onConnection(fn(conn: ServerConn))Register per-connection handler.
server.listen()fn listen()Bind and run accept loop (blocks).
server.broadcast(room, msg)fn broadcast(room: string, msg: string)Send msg to all conns in room.
server.connections()fn connections(): ServerConn[]Snapshot of active connections.
server.close()fn close()Stop accepting; close all connections.
conn.sendText(text)Send text frame to this client.
conn.sendBinary(bytes)Send binary frame.
conn.receive()Receive next frame from this client.
conn.close(code, reason)Close this connection.
conn.remoteAddrstringPeer ip:port.
conn.pathstringRequest path from handshake (/chat).
conn.headersMapHandshake request headers.
conn.idu64Unique connection identifier.

ServerOpts

FieldTypeDefaultDescription
portu16requiredListen port.
bind_addressstring"127.0.0.1"Bind host/IP.
max_connectionsusize128Maximum concurrent clients.
max_message_sizeusize1_000_000Max reassembled message size.
idle_timeoutu64 (ms)30000Per-connection idle timeout.
tlsTLS.ConfigTLS termination for wss://.

5. Frame Types & Event Handling

WebSocket messages contain a type property corresponding to RFC 6455 frame opcodes:

Frame TypeOpcodeDescriptionPayload
"text"0x1UTF-8 text payloadstring (validated UTF-8)
"binary"0x2Raw binary bufferu8[]
"ping"0x9Control frame for keepalive heartbeatu8[] (≤125 bytes)
"pong"0xAResponse to pingu8[] (≤125 bytes)
"close"0x8Initiates connection termination{ code: u16, reason: string }
"continuation"0x0Continuation of fragmented messageinternal — reassembled before delivery

Data frames (text/binary) may be fragmented across multiple frames (FIN=0 + continuation). The library reassembles fragments transparently; receive() always returns a complete message.

Close Codes

CodeNameMeaning
1000Normal ClosureSuccessful completion.
1001Going AwayServer/client going away.
1002Protocol ErrorProtocol violation.
1003Unsupported DataEndpoint can't handle data type.
1006Abnormal ClosureNo close frame (network drop).
1007Invalid PayloadNon-UTF-8 in text frame.
1008Policy ViolationGeneric policy violation.
1009Message Too BigMessage exceeds max_message_size.
1011Internal ErrorServer error.
1012Service RestartServer restarting.
1013Try Again LaterTemporary overload.

6. TLS / Secure WebSocket (wss://)

scheme determines TLS:

URL SchemeTransportPort DefaultTLS
ws://plain TCP80none
wss://TLS over TCP443via TLS module
// wss:// client — TLS verification enabled
let client = WebSocket.connect("wss://echo.example.com:443", {
tls: { verifyPeer: true, alpn: ["http/1.1"] }
});

// wss:// server — TLS termination
let server = WebSocket.server({
port: 8443,
tls: { cert: "/path/cert.pem", key: "/path/key.pem" }
});
server.startEcho();

TLS handshake precedes the WebSocket Upgrade — the TLS module handles cert verification, ALPN (http/1.1), and STARTTLS-style wrapping diagnostics.


7. Error Handling

ErrorWhenRecovery
HandshakeFailedkey mismatch, bad status, missing Upgrade headerCheck server is WS-capable; inspect error.message
ConnectionRefusedTCP connect failedRetry with backoff; verify host/port
Timeoutconnect_timeout or idle_timeout exceededIncrease timeout; check network
MessageTooBigpayload > max_message_sizeRaise limit or fragment application payload
ProtocolErrorinvalid opcode, unmasked client frame, bad close codeIndicates bug — report
TlsErrorcert verify failed, handshake failedCheck cert chain; see TLS diagnostics
Closedoperation after close()Guard with isConnected()
let client = WebSocket.connect("ws://127.0.0.1:8099", { connect_timeout: 2000 });
if (WebSocket.isError(client)) {
print("Connect error:", client.kind, client.message);
if (client.kind == "Timeout") {
print("Retrying...");
}
}

8. Complete Example — Chat Client + Server

// === server.adesh ===
import WebSocket;

let server = WebSocket.server({ port: 8099, bind_address: "127.0.0.1" });
server.onConnection(fn(conn) {
print(f"Client {conn.id} connected from {conn.remoteAddr}");
conn.sendText("Welcome to AdeshLang chat!");
while (conn.isConnected()) {
let msg = conn.receive();
if (msg.type == "text") {
print(f"[{conn.id}] {msg.payload}");
// broadcast to all
for c in server.connections() {
if (c.id != conn.id) { c.sendText(f"[{conn.id}]: {msg.payload}"); }
}
} else if (msg.type == "close") { break; }
}
print(f"Client {conn.id} disconnected");
});
server.listen();

// === client.adesh ===
import WebSocket;

let client = WebSocket.connect("ws://127.0.0.1:8099", { connect_timeout: 5000 });
if (!WebSocket.isError(client)) {
client.sendText("Hello, chat!");
let reply = client.receive();
print("Server:", reply.payload);
// heartbeat
client.sendPing([0x01]);
let pong = client.receive();
print("Pong:", pong.type);
client.close(1000, "Done");
}

9. Project Examples

  • Net Library — raw TCP/UDP primitives under WebSocket
  • TLS Library — certificate verification and wss://
  • HTTP Library — the Upgrade is an HTTP request
  • Source: src/runtime/stdlib_src/websocket/ and src/runtime/stdlib_src/tls/