WebSocket Relay
The WebSocket relay middleware upgrades HTTP connections to WebSocket and relays messages to a target process.
How It Works
- HTTP handler sets
X-WS-Relayheader with target process PID - Middleware upgrades connection to WebSocket
- Relay attaches to the target process and monitors it
- Messages flow bidirectionally between client and process
Process Semantics
WebSocket connections are full processes with their own PID. They integrate with the process system:
- Addressable → Any process can send messages to a WebSocket PID
- Monitorable → Processes can monitor WebSocket connections for exit events
- Linkable → WebSocket connections can be linked to other processes
- EXIT events → When connection closes, monitors receive exit notifications
-- Monitor a WebSocket connection from another process
process.monitor(websocket_pid)
-- Send message to WebSocket client from any process
process.send(websocket_pid, "ws.send", {type = "text", data = "hello"})
Connection Transfer
Connections can be transferred to a different process by sending a control message:
process.send(websocket_pid, "ws.control", {
target_pid = new_process_pid,
message_topic = "ws.message"
})
Configuration
Add as post-match middleware on a router:
- name: ws_router
kind: http.router
meta:
server: gateway
prefix: /ws
post_middleware:
- websocket_relay
post_options:
wsrelay.allowed.origins: "https://app.example.com"
| Option | Description |
|---|---|
wsrelay.allowed.origins |
Comma-separated allowed origins |
Handler Setup
The HTTP handler spawns a process and configures the relay:
local http = require("http")
local json = require("json")
local function handler()
local req = http.request()
local res = http.response()
-- Spawn handler process
local pid = process.spawn("app.ws:handler", "app:processes")
-- Configure relay
res:header("X-WS-Relay", json.encode({
target_pid = tostring(pid),
message_topic = "ws.message",
heartbeat_interval = "30s",
metadata = {
user_id = req:query("user_id")
}
}))
end
Relay Config Fields
| Field | Type | Default | Description |
|---|---|---|---|
target_pid |
string | required | Process PID to receive messages |
message_topic |
string | ws.message |
Topic for client messages |
heartbeat_interval |
duration | - | Heartbeat frequency (e.g. 30s) |
metadata |
object | - | Attached to all messages |
Message Topics
The relay sends these messages to the target process:
| Topic | When | Payload |
|---|---|---|
ws.join |
Client connects | client_pid, metadata |
ws.message |
Client sends message | client_pid, type, data, metadata |
ws.heartbeat |
Periodic (if configured) | client_pid, uptime, message_count |
ws.leave |
Client disconnects | client_pid, reason, metadata |
Receiving Messages
local json = require("json")
local function handler()
local inbox = process.inbox()
while true do
local msg, ok = inbox:receive()
if not ok then break end
local topic = msg:topic()
local data = msg:payload():data()
if topic == "ws.join" then
-- Client connected
local client_pid = data.client_pid
elseif topic == "ws.message" then
-- Handle client message
local content = json.decode(data.data)
handle_message(data.client_pid, content)
elseif topic == "ws.leave" then
-- Client disconnected
cleanup(data.client_pid)
end
end
end
Sending to Client
Send messages back using the client PID:
-- Send text message
process.send(client_pid, "ws.send", {
type = "text",
data = json.encode({event = "update", value = 42})
})
-- Send binary
process.send(client_pid, "ws.send", {
type = "binary",
data = binary_content
})
-- Close connection
process.send(client_pid, "ws.close", {
code = 1000,
reason = "Session ended"
})
Broadcasting
Track client PIDs to broadcast to multiple clients:
local clients = {}
-- On join
clients[client_pid] = true
-- On leave
clients[client_pid] = nil
-- Broadcast
local function broadcast(message)
local data = json.encode(message)
for pid, _ in pairs(clients) do
process.send(pid, "ws.send", {type = "text", data = data})
end
end
See Also
- Middleware - Middleware configuration
- Process - Process messaging
- WebSocket Client - Outbound WebSocket connections