Skip to content

websocket-server

stable

Low-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, …}
14 functions Networking
/ filter jk navigate Esc clear
Functions (14)
  1. compute_accept_key Compute the Sec-WebSocket-Accept response key
  2. build_upgrade_response Build the HTTP 101 upgrade response string
  3. encode_text_frame Encode a text string as a WebSocket frame
  4. encode_binary_frame Encode binary data as a WebSocket frame
  5. encode_close_frame Encode a close frame with code and reason
  6. encode_ping_frame Encode a ping control frame
  7. encode_pong_frame Encode a pong control frame
  8. decode_frame Decode a raw WebSocket frame from bytes
  9. unmask_payload XOR-unmask a payload using a 4-byte mask key
  10. parse_upgrade_request Parse HTTP upgrade request headers
  11. validate_upgrade_request Validate an HTTP WebSocket upgrade request
  12. broadcast_encode Encode a text frame for broadcasting to multiple clients
  13. frame_type_name Get the name of a WebSocket opcode
  14. 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)}")
enespt-br