WebSocket Client
WebSocket Client
Section titled “WebSocket Client”WebSocket client for real-time bidirectional communication with servers.
Loading
Section titled “Loading”local websocket = require("websocket")Connecting
Section titled “Connecting”Basic Connection
Section titled “Basic Connection”local client, err = websocket.connect("wss://api.example.com/ws")if err then return nil, errendWith Options
Section titled “With Options”local client, err = websocket.connect("wss://api.example.com/ws", { headers = { ["Authorization"] = "Bearer " .. token }, protocols = {"graphql-ws"}, dial_timeout = "10s", read_timeout = "30s", compression = websocket.COMPRESSION.CONTEXT_TAKEOVER})| Parameter | Type | Description |
|---|---|---|
url | string | WebSocket URL (ws:// or wss://) |
options | table | Connection options (optional) |
Returns: Client, error
Connection Options
Section titled “Connection Options”| Option | Type | Description |
|---|---|---|
headers | table | HTTP headers for handshake |
protocols | table | WebSocket subprotocols |
dial_timeout | number/string | Connection timeout (ms or “5s”) |
read_timeout | number/string | Read timeout |
write_timeout | number/string | Write timeout |
compression | number | Compression mode (see Constants) |
compression_threshold | number | Min size to compress (0-100MB) |
read_limit | number | Max message size (0-128MB) |
channel_capacity | number | Receive channel buffer (1-10000) |
Timeout format: Numbers are milliseconds, strings use Go duration format (“5s”, “1m”).
Sending Messages
Section titled “Sending Messages”Text Messages
Section titled “Text Messages”client:send("Hello, Server!")
-- Send JSONclient:send(json.encode({ type = "subscribe", channel = "orders"}))Binary Messages
Section titled “Binary Messages”client:send(binary_data, websocket.BINARY)| Parameter | Type | Description |
|---|---|---|
data | string | Message content |
type | number | websocket.TEXT (1) or websocket.BINARY (2) |
Yields until the message is sent.
Returns: boolean, error
client:ping()Yields until the ping is sent.
Returns: boolean, error
Receiving Messages
Section titled “Receiving Messages”The channel() method returns a channel for receiving messages. receive() is an alias for channel(). Works with channel.select for multiplexing.
Basic Receive
Section titled “Basic Receive”local ch = client:channel()
local msg, ok = ch:receive()if ok then print("Type:", msg.type) -- "text" or "binary" print("Data:", msg.data)endMessage Loop
Section titled “Message Loop”local ch = client:channel()
while true do local msg, ok = ch:receive() if not ok then break -- Connection closed end
if msg.type == "text" then local data = json.decode(msg.data) handle_message(data) endendWith Select
Section titled “With Select”local ch = client:channel()local timeout = time.after("30s")
while true do local r = channel.select { ch:case_receive(), timeout:case_receive() }
if r.channel == timeout then client:ping() -- Keep-alive timeout = time.after("30s") else local data = json.decode(r.value.data) process(data) endendMessage Object
Section titled “Message Object”| Field | Type | Description |
|---|---|---|
type | string | "text" or "binary" |
data | string? | Message content (nil for unknown payload types) |
Closing Connection
Section titled “Closing Connection”-- Normal close (code 1000)client:close()
-- With code and reasonclient:close(websocket.CLOSE_CODES.NORMAL, "Session ended")
-- Error closeclient:close(websocket.CLOSE_CODES.INTERNAL_ERROR, "Processing failed")| Parameter | Type | Description |
|---|---|---|
code | number | Close code (1000-4999), default 1000 |
reason | string | Close reason (optional) |
Yields until the close frame is sent.
Constants
Section titled “Constants”Message Types
Section titled “Message Types”-- Numeric (for send)websocket.TEXT -- 1websocket.BINARY -- 2
-- String (received message type field)websocket.TYPE_TEXT -- "text"websocket.TYPE_BINARY -- "binary"websocket.TYPE_PING -- "ping"websocket.TYPE_PONG -- "pong"websocket.TYPE_CLOSE -- "close"Compression Modes
Section titled “Compression Modes”websocket.COMPRESSION.DISABLED -- 0 (no compression)websocket.COMPRESSION.CONTEXT_TAKEOVER -- 1 (sliding window)websocket.COMPRESSION.NO_CONTEXT -- 2 (per-message)Close Codes
Section titled “Close Codes”| Constant | Code | Description |
|---|---|---|
NORMAL | 1000 | Normal closure |
GOING_AWAY | 1001 | Server shutting down |
PROTOCOL_ERROR | 1002 | Protocol error |
UNSUPPORTED_DATA | 1003 | Unsupported data type |
RESERVED | 1004 | Reserved |
NO_STATUS | 1005 | No status received |
ABNORMAL_CLOSURE | 1006 | Connection lost |
INVALID_PAYLOAD | 1007 | Invalid frame payload |
POLICY_VIOLATION | 1008 | Policy violation |
MESSAGE_TOO_BIG | 1009 | Message too large |
MANDATORY_EXTENSION | 1010 | Required extension not negotiated |
INTERNAL_ERROR | 1011 | Server error |
SERVICE_RESTART | 1012 | Server restarting |
TRY_AGAIN_LATER | 1013 | Server overloaded |
BAD_GATEWAY | 1014 | Gateway error |
TLS_HANDSHAKE | 1015 | TLS handshake failure |
client:close(websocket.CLOSE_CODES.NORMAL, "Done")Examples
Section titled “Examples”Real-Time Chat
Section titled “Real-Time Chat”local function connect_chat(room_id, on_message) local client, err = websocket.connect("wss://chat.example.com/ws", { headers = {["Authorization"] = "Bearer " .. token} }) if err then return nil, err end
-- Join room client:send(json.encode({ type = "join", room = room_id }))
-- Message loop local ch = client:channel() while true do local msg, ok = ch:receive() if not ok then break end
local data = json.decode(msg.data) on_message(data) end
client:close()endPrice Stream with Keep-Alive
Section titled “Price Stream with Keep-Alive”local client = websocket.connect("wss://stream.example.com/prices")
client:send(json.encode({ action = "subscribe", symbols = {"BTC-USD", "ETH-USD"}}))
local ch = client:channel()local heartbeat = time.after("30s")
while true do local r = channel.select { ch:case_receive(), heartbeat:case_receive() }
if r.channel == heartbeat then client:ping() heartbeat = time.after("30s") elseif not r.ok then break -- Connection closed else local price = json.decode(r.value.data) update_price(price.symbol, price.value) endend
client:close()Permissions
Section titled “Permissions”WebSocket connections are subject to security policy evaluation.
Security Actions
Section titled “Security Actions”| Action | Resource | Description |
|---|---|---|
websocket.connect | - | Allow/deny WebSocket connections |
websocket.connect.url | URL | Allow/deny connections to specific URLs |
See Security Model for policy configuration.
Errors
Section titled “Errors”| Condition | Kind | Retryable |
|---|---|---|
| Connections disabled | errors.PERMISSION_DENIED | no |
| URL not allowed | errors.PERMISSION_DENIED | no |
| No context | errors.INTERNAL | no |
| Connection failed | errors.INTERNAL | yes |
| Invalid connection ID | errors.INTERNAL | no |
local client, err = websocket.connect(url)if err then if errors.is(err, errors.PERMISSION_DENIED) then print("Access denied:", err:message()) elseif err:retryable() then print("Temporary error:", err:message()) end return nil, errendSee Error Handling for working with errors.