WebSocket Reference
Overview
Zigmund provides built-in WebSocket support with configurable timeouts, ping/pong management, subprotocol negotiation, and origin validation.
Handler Signature
WebSocket handlers must conform to one of these signatures:
// Full signature with request access
fn handler(conn: *Connection, req: *Request, allocator: std.mem.Allocator) !void
// Legacy signature without request
fn handler(conn: *Connection, allocator: std.mem.Allocator) !void
The handler receives a Connection for sending and receiving messages.
Connection
Sending Messages
pub fn sendText(self: *Connection, text: []const u8) !void
pub fn sendBinary(self: *Connection, payload: []const u8) !void
pub fn ping(self: *Connection, payload: []const u8) !void
Receiving Messages
pub fn receiveSmall(self: *Connection) !Message
pub fn receiveSmallWithTimeout(self: *Connection, timeout_ms: u64) !Message
Returns a Message with opcode and data fields.
Message Type
pub const Message = struct {
opcode: Opcode,
data: []u8,
};
Opcode values include .text, .binary, .ping, .pong, .close.
Connection Configuration
pub fn setIdleTimeoutMs(self: *Connection, timeout_ms: ?u64) void
pub fn setAutoPong(self: *Connection, enabled: bool) void
pub fn setPingPolicy(self: *Connection, ping_interval_ms: ?u64, pong_timeout_ms: ?u64) void
pub fn setMaxMessageBytes(self: *Connection, max: ?usize) void
pub fn setSendTimeoutMs(self: *Connection, timeout_ms: ?u64) void
Subprotocol
pub fn subprotocol(self: *const Connection) ?[]const u8
Returns the negotiated subprotocol, if any.
Close
pub fn closeWithCode(self: *Connection, code: u16, reason: []const u8) !void
pub fn lastCloseCode(self: *const Connection) ?u16
Route Registration
try app.websocket("/ws", handler, .{
.idle_timeout_ms = 30000,
.auto_pong = true,
.ping_interval_ms = 15000,
.pong_timeout_ms = 5000,
.max_message_bytes = 65536,
.allowed_origins = &.{"https://example.com"},
.subprotocols = &.{"graphql-ws"},
});
WebSocketRouteOptions
| Field | Type | Default | Description |
|---|---|---|---|
name |
?[]const u8 |
null |
Route name |
summary |
?[]const u8 |
null |
OpenAPI summary |
description |
?[]const u8 |
null |
OpenAPI description |
idle_timeout_ms |
?u64 |
null |
Disconnect after this many idle ms |
auto_pong |
bool |
true |
Automatically respond to pings |
ping_interval_ms |
?u64 |
null |
Send pings at this interval |
pong_timeout_ms |
?u64 |
null |
Timeout waiting for pong |
max_message_bytes |
?usize |
null |
Maximum message size |
max_pending_messages |
?usize |
null |
Maximum pending messages in queue |
send_timeout_ms |
?u64 |
null |
Send operation timeout |
allowed_origins |
[]const []const u8 |
&.{} |
Allowed Origin headers (empty = allow all) |
subprotocols |
[]const []const u8 |
&.{} |
Supported subprotocols |
require_subprotocol |
bool |
false |
Reject if no subprotocol matches |
dependencies |
[]const DependencySpec |
&.{} |
Route dependencies |
openapi_security |
[]const OpenApiSecurityAlternative |
&.{} |
Security requirements |
deprecated |
bool |
false |
Mark as deprecated |
operation_id |
?[]const u8 |
null |
OpenAPI operation ID |
openapi_extensions |
[]const OpenApiExtension |
&.{} |
x- extensions |
Example
fn chatHandler(conn: *zigmund.runtime.websocket.Connection, req: *zigmund.Request, allocator: std.mem.Allocator) !void {
_ = req;
_ = allocator;
while (true) {
const msg = conn.receiveSmall() catch |err| {
if (err == error.ConnectionClosed) return;
return err;
};
switch (msg.opcode) {
.text => try conn.sendText(msg.data),
.binary => try conn.sendBinary(msg.data),
.close => return,
else => {},
}
}
}