websocket-server
stableLow-level WebSocket server utilities for RFC 6455 frame encoding/decoding, handshake negotiation, and payload masking. Use this to build WebSocket servers from raw TCP streams.
use plugin websocket-server::{compute_accept_key, build_upgrade_response, encode_text_frame, …} Functions (14)
- compute_accept_key Compute the Sec-WebSocket-Accept response key
- build_upgrade_response Build the HTTP 101 upgrade response string
- encode_text_frame Encode a text string as a WebSocket frame
- encode_binary_frame Encode binary data as a WebSocket frame
- encode_close_frame Encode a close frame with code and reason
- encode_ping_frame Encode a ping control frame
- encode_pong_frame Encode a pong control frame
- decode_frame Decode a raw WebSocket frame from bytes
- unmask_payload XOR-unmask a payload using a 4-byte mask key
- parse_upgrade_request Parse HTTP upgrade request headers
- validate_upgrade_request Validate an HTTP WebSocket upgrade request
- broadcast_encode Encode a text frame for broadcasting to multiple clients
- frame_type_name Get the name of a WebSocket opcode
- close_code_reason Get the standard reason string for a close code
Overview
websocket-server is a stateless, dependency-free toolkit for implementing the
RFC 6455 WebSocket protocol on top of a raw TCP stream. It does not own sockets
or manage connections — instead it gives you the pure building blocks: hashing the
client key for the handshake, framing outgoing messages, and decoding incoming
frames back into their opcode and payload. Every function takes plain strings or
bytes and returns plain strings or bytes, so you stay in full control of the I/O
loop.
The mental model is a two-phase lifecycle. First the handshake: validate and parse the client's HTTP upgrade request, then reply with the 101 Switching Protocols response built from the computed accept key. After that the frame loop: encode text, binary, or control frames to send, and decode the bytes you read from the socket — automatically unmasking client payloads. Use this plugin when you need a WebSocket server but want to drive the transport yourself rather than depend on a full framework.
Common patterns
Complete the opening handshake from a raw HTTP request:
use plugin websocket-server::{validate_upgrade_request, parse_upgrade_request, build_upgrade_response}
if validate_upgrade_request(http_text) {
let req = parse_upgrade_request(http_text)
print("client wants: {req["path"]}")
let response = build_upgrade_response(req["key"])
// write response over the TCP socket, then enter the frame loop
}
Decode an incoming frame and react to its opcode:
use plugin websocket-server::{decode_frame, encode_text_frame, encode_pong_frame, encode_close_frame}
let frame = decode_frame(raw_bytes)
if frame["opcode_name"] == "text" {
let reply = encode_text_frame("echo: {frame["payload"]}")
// write reply to the client
} else if frame["opcode_name"] == "ping" {
let pong = encode_pong_frame(frame["payload"])
} else if frame["opcode_name"] == "close" {
let bye = encode_close_frame(1000, "Normal closure")
}
Broadcast a single encoded message to every connected client:
use plugin websocket-server::{broadcast_encode}
let frame = broadcast_encode("server event: new user joined")
// write the same frame bytes to each client socket in your connection pool
Compute the Sec-WebSocket-Accept response key
Computes the Sec-WebSocket-Accept value by hashing the client's key with the RFC 6455 GUID using SHA-1 and encoding the result as base64. Pass the value of the Sec-WebSocket-Key request header.
use plugin websocket-server::{compute_accept_key}
let accept = compute_accept_key("dGhlIHNhbXBsZSBub25jZQ==")
print("Accept: {accept}")
Use the key extracted from a parsed request to drive the handshake yourself:
use plugin websocket-server::{parse_upgrade_request, compute_accept_key}
let req = parse_upgrade_request(http_text)
let accept = compute_accept_key(req["key"])
print("Sec-WebSocket-Accept: {accept}")
Build the HTTP 101 upgrade response string
Builds the complete HTTP 101 Switching Protocols response string ready to send over a TCP socket. Combines the computed accept key with the required upgrade headers.
use plugin websocket-server::{build_upgrade_response}
let response = build_upgrade_response("dGhlIHNhbXBsZSBub25jZQ==")
// Send response over TCP socket
Encode a text string as a WebSocket frame
Encodes a UTF-8 string as a finalized WebSocket text frame (opcode 0x1, FIN bit set, no masking). Returns the raw frame bytes to write to the client socket.
use plugin websocket-server::{encode_text_frame}
let frame = encode_text_frame("Hello, client!")
// Write frame bytes to client TCP stream
Interpolate dynamic values into the message before framing it:
use plugin websocket-server::{encode_text_frame}
let user = "alice"
let count = 3
let frame = encode_text_frame("{user} has {count} unread messages")
// Write frame bytes to the client socket
Encode binary data as a WebSocket frame
Encodes a byte slice as a finalized WebSocket binary frame (opcode 0x2). Use for sending structured binary data such as serialized messages.
use plugin websocket-server::{encode_binary_frame}
let payload = json.stringify(#{"type": "update", "value": 42})
let frame = encode_binary_frame(payload)
Encode a close frame with code and reason
Encodes a WebSocket close frame with the given status code and optional reason string. Standard close codes: 1000 (normal), 1001 (going away), 1011 (server error).
use plugin websocket-server::{encode_close_frame}
let frame = encode_close_frame(1000, "Normal closure")
// Write to client to initiate graceful shutdown
Encode a ping control frame
Encodes a ping control frame (opcode 0x9). Pass an empty string or bytes for a bare ping. The client should respond with a matching pong frame.
use plugin websocket-server::{encode_ping_frame}
let frame = encode_ping_frame("")
// Send periodically to detect dead connections
Encode a pong control frame
Encodes a pong control frame (opcode 0xA). When a ping is received from a client, echo its payload back in a pong frame.
use plugin websocket-server::{encode_pong_frame, decode_frame}
let incoming = decode_frame(raw_bytes)
if incoming["opcode_name"] == "ping" {
let pong = encode_pong_frame(incoming["payload"])
}
Decode a raw WebSocket frame from bytes
Parses a raw WebSocket frame and returns a table with opcode (integer), opcode_name (string), payload (bytes), fin (bool), and masked (bool). Automatically unmasks client frames.
use plugin websocket-server::{decode_frame}
let frame = decode_frame(raw_bytes)
if frame["opcode_name"] == "text" {
print("received: {frame["payload"]}")
}
Inspect the frame metadata to handle fragmentation and control frames:
use plugin websocket-server::{decode_frame}
let frame = decode_frame(raw_bytes)
print("opcode {frame["opcode"]} ({frame["opcode_name"]})")
print("fin: {frame["fin"]}, masked: {frame["masked"]}")
if !frame["fin"] {
print("partial message — more continuation frames to come")
}
XOR-unmask a payload using a 4-byte mask key
Applies the RFC 6455 XOR masking transform to payload using the 4-byte mask_key. Use this when you have already separated the mask key from the payload bytes.
use plugin websocket-server::{unmask_payload}
let clear = unmask_payload(masked_bytes, mask_key)
Parse HTTP upgrade request headers
Parses a raw HTTP upgrade request string and extracts the fields needed for WebSocket handshake negotiation. Returns a table with the request line fields and relevant headers.
use plugin websocket-server::{parse_upgrade_request, build_upgrade_response}
let req = parse_upgrade_request(http_text)
print("path: {req["path"]}")
let response = build_upgrade_response(req["key"])
Validate an HTTP WebSocket upgrade request
Returns true if the HTTP request string is a valid WebSocket upgrade request: GET method, Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key, and Sec-WebSocket-Version headers are all present.
use plugin websocket-server::{validate_upgrade_request}
if validate_upgrade_request(http_text) {
print("valid WebSocket upgrade")
}
Encode a text frame for broadcasting to multiple clients
Encodes a text string as a WebSocket frame optimised for broadcasting to multiple clients. Functionally identical to encode_text_frame but named for clarity in broadcast contexts.
use plugin websocket-server::{broadcast_encode}
let frame = broadcast_encode("server event: connected")
// Write frame to every connected client
Get the name of a WebSocket opcode
Returns the human-readable name for a WebSocket opcode integer: "text", "binary", "close", "ping", "pong", "continuation", or "unknown".
use plugin websocket-server::{frame_type_name}
print(frame_type_name(1)) // "text"
print(frame_type_name(8)) // "close"
Get the standard reason string for a close code
Returns the standard RFC 6455 reason phrase for a numeric close status code. For example, 1000 → "Normal Closure", 1011 → "Internal Server Error".
use plugin websocket-server::{close_code_reason}
print(close_code_reason(1000)) // "Normal Closure"
print(close_code_reason(1002)) // "Protocol Error"
Pair it with decode_frame to log why a client disconnected. The first two
payload bytes of a close frame carry the status code:
use plugin websocket-server::{decode_frame, close_code_reason}
let frame = decode_frame(raw_bytes)
if frame["opcode_name"] == "close" {
print("client closed connection")
}
print("1006 means: {close_code_reason(1006)}")