websocket API reference Changelog View on GitHub

API reference

WebSocket frames and the upgrade handshake in Zig, with no IO and no allocator.

Callers provide all buffers. The parser handles header parsing, payload length decoding, and masking. Fragmentation sequencing and UTF-8 validation are the caller's responsibility.

Quick start

Import the server or client namespace depending on your role:

const ws = @import("websocket").server;

var parser: ws.Parser = .init(.{});
while (true) {
    const result = try parser.feed(buf);
    buf = buf[result.consumed..];
    switch (result.event) {
        .frame_header => |hdr| { ... },
        .payload => |data| { ... },
        .frame_end => {},
        .need_more => break,
    }
}

module client

src

Client-role API for parsing unmasked inbound frames and writing masked outbound frames.

Constants

pub const AcceptKey = frame.AcceptKey

src

Buffer type for a base64-encoded Sec-WebSocket-Accept value.

pub const MaskKey = frame.MaskKey

src

Four-byte masking key used by client frames.

pub const ParseError = frame.ParseError

src

Protocol errors reported while parsing inbound frames.

pub const max_control_payload_len = frame.max_control_payload_len

src

Maximum payload length for WebSocket control frames (ping, pong, close).

pub const MessageWriter = @import("message_writer.zig").ClientMessageWriter

src

Fragmented message writer that masks each emitted frame.

pub const Parser = parse.ClientParser

src

Frame parser for unmasked frames received by a client.

pub const FrameHandler = parse.ClientFrameHandler

src

Message-oriented parser for client connections.

Functions

pub fn parseClosePayload(payload: []const u8) ClosePayloadError!?ClosePayload

src

Parse and validate a close frame payload.

pub fn generateMaskKey() MaskKey

src

Generate a cryptographically random masking key.

pub fn computeAcceptKey(key: []const u8, out: *AcceptKey) []const u8

src

Compute the expected Sec-WebSocket-Accept header value for a client handshake.

pub fn writeFrame( writer: *Io.Writer, payload: []u8, options: WriteFrameOptions, ) Io.Writer.Error!void

src

Write a complete masked frame (header + payload) to writer. Clients MUST mask all frames per RFC 6455 Section 5.3.

payload is mutable because masking is applied in-place before writing. The buffer contents will be XOR'd with the mask key after this call returns. Callers who need to retain the original data should copy it first.

pub fn writeClose(writer: *Io.Writer, code: CloseCode) Io.Writer.Error!void

src

Write a masked close frame with a status code (no reason text).

pub fn writePing(writer: *Io.Writer, payload: []u8) Io.Writer.Error!void

src

Write a masked ping frame with an optional payload.

pub fn writePong(writer: *Io.Writer, payload: []u8) Io.Writer.Error!void

src

Write a masked pong frame with the given payload (typically echoed from a ping).

Types

enum CloseCode

src
pub const CloseCode = close.CloseCode

Close status codes defined by RFC 6455 Section 7.4.

Fields

normal normal = 1000
going_away going_away = 1001
protocol_error protocol_error = 1002
unsupported_data unsupported_data = 1003
no_status no_status = 1005
abnormal abnormal = 1006
invalid_payload invalid_payload = 1007
policy_violation policy_violation = 1008
too_big too_big = 1009
mandatory_extension mandatory_extension = 1010
internal_error internal_error = 1011
tls_handshake tls_handshake = 1015
_ _

Functions

pub fn isValid(self: CloseCode) bool

src

RFC 6455 Section 7.4: validate whether a close code may appear on the wire. Codes 1005, 1006, and 1015 are reserved for local use and MUST NOT be sent.

pub fn toBytes(self: CloseCode) [2]u8

src

Encode the status code as two big-endian bytes for a close frame payload.

struct ClosePayload

src
pub const ClosePayload = close.ClosePayload

Parsed close frame payload containing the status code and optional reason bytes.

Fields

code: CloseCode
reason: []const u8

union Extension

src
pub const Extension = @import("extension.zig").Extension

Supported negotiated WebSocket extensions.

Fields

permessage_deflate: DeflateConfig

Constants

pub const max_header_len = 256

src

pub const HeaderBuffer = frame.BoundedBuffer(max_header_len)

src

Functions

pub fn claimedRsv(self: Extension) RsvBits

src

pub fn allowedRsv(ext: ?Extension) RsvBits

src

pub fn writeHeader(self: Extension, buf: *HeaderBuffer) void

src

Types

struct DeflateConfig

src

Fields

Constants

pub const init: DeflateConfig = …

src
value
.{
            .server_no_context_takeover = false,
            .client_no_context_takeover = false,
            .server_max_window_bits = .@"15",
            .client_max_window_bits = .@"15",
        }

enum WindowBits

src
pub const WindowBits = enum(u4)

Fields

@"8" @"8" = 8
@"9" @"9" = 9
@"10" @"10" = 10
@"11" @"11" = 11
@"12" @"12" = 12
@"13" @"13" = 13
@"14" @"14" = 14
@"15" @"15" = 15

enum Opcode

src
pub const Opcode = frame.Opcode

WebSocket frame opcode.

Fields

continuation continuation = 0x0
text text = 0x1
binary binary = 0x2
close close = 0x8
ping ping = 0x9
pong pong = 0xA
_ _

Functions

pub fn isControl(self: Opcode) bool

src

Returns true for opcodes 0x8–0xF (control frames: close, ping, pong, and reserved).

pub fn isReserved(self: Opcode) bool

src

Returns true for opcodes not defined by RFC 6455 (i.e. not continuation, text, binary, close, ping, or pong).

struct FrameHeader

src
pub const FrameHeader = frame.FrameHeader

Decoded frame header and its serialized buffer helper.

Fields

fin: bool = true

True if this is the final fragment in a message.

rsv: RsvBits = .empty

Reserved bits for extensions.

opcode: Opcode

The frame opcode.

payload_len: u64

Length of the payload data in bytes.

mask_key: ?MaskKey = null

The masking key, or null for unmasked frames.

Functions

pub fn encodedLen(self: FrameHeader) usize

src

Returns the total wire size of this header (2..14 bytes).

Types

struct Buffer

src

A serialized frame header ready to write to the wire.

Fields

buf: HeaderBuffer

Functions

pub fn init(header: FrameHeader) Buffer

src

Serialize a FrameHeader into its wire representation.

pub fn constSlice(self: *const Buffer) []const u8

src

Returns the serialized header bytes.

pub fn write(self: *const Buffer, writer: *Io.Writer) Io.Writer.Error!void

src

Writes the serialized header to writer.

struct Mask

src
pub const Mask = frame.Mask

Stateful XOR mask for masking and unmasking payload bytes.

Fields

key: MaskKey
offset: u2

Constants

pub const none: Mask = .{ .key = @splat(0), .offset = 0 }

src

A no-op mask (all-zero key). XOR with zero leaves data unchanged.

Functions

pub fn init(key: MaskKey) Mask

src

Create a mask from key with the offset starting at zero.

pub fn apply(self: *Mask, payload: []u8) void

src

Apply the mask to payload in-place, advancing the internal offset. If the key is all zeros (unmasked), the XOR is a no-op.

struct RsvBits

src
pub const RsvBits = frame.RsvBits

RSV bitset from the frame header.

Fields

rsv3: bool = false
rsv2: bool = false
rsv1: bool = false

Constants

pub const empty: RsvBits = .{}

src

All bits unset (the default for frames without extensions).

pub const permessage_deflate: RsvBits = .{ .rsv1 = true }

src

RSV1 set, as required by permessage-deflate (RFC 7692 §6.1).

Functions

pub fn areSet(self: RsvBits) bool

src

Returns true if any RSV bit is set.

pub fn hasDisallowed(self: RsvBits, allowed: RsvBits) bool

src

Returns true if any bit is set that is not in allowed.

union Event

src
pub const Event = frame.Event

Streaming parser event.

Fields

frame_header: FrameHeader

A complete frame header has been parsed.

payload: []u8

A chunk of payload data (a slice into the caller's input buffer, already unmasked).

frame_end frame_end

The current frame is complete. The parser is ready for the next frame.

need_more need_more

More input is needed before the next event can be produced.

struct WriteFrameOptions

src
pub const WriteFrameOptions = frame.WriteFrameOptions

Options for frame-writing helpers.

Fields

opcode: Opcode

Frame opcode. Only non-reserved opcodes are valid.

compressed: bool = false

Set when the payload has been compressed with permessage-deflate. Controls the RSV1 bit on the first frame.

struct Options

src
pub const Options = parse.Options

Parser options shared by both roles.

Fields

extension: ?Extension = null

Negotiated extension that is allowed to claim RSV bits during parsing.

struct MessageValidator

src
pub const MessageValidator = parse.MessageValidator

Fragmentation validator shared by both roles.

Fields

state: State

Constants

pub const init: MessageValidator = .{ .state = .idle }

src

Initial state, ready for the first data frame.

pub const ValidationError = …

src
value
error{
        /// Continuation frame received with no message in progress.
        UnexpectedContinuation,
        /// Text/binary frame received while a fragmented message is in progress.
        ExpectedContinuation,
    }

Errors returned by check for fragmentation protocol violations.

Functions

pub fn check(self: *MessageValidator, header: FrameHeader) ValidationError!void

src

Validate a frame header against fragmentation rules. Control frames are always allowed (they can be interleaved mid-fragment).

module server

src

Server-role API for parsing masked inbound frames, writing unmasked outbound frames, and handling upgrades.

Constants

pub const MaskKey = frame.MaskKey

src

Four-byte masking key used by client frames.

pub const ParseError = frame.ParseError

src

Protocol errors reported while parsing inbound frames.

pub const AcceptKey = frame.AcceptKey

src

Buffer type for a base64-encoded Sec-WebSocket-Accept value.

pub const max_control_payload_len = frame.max_control_payload_len

src

Maximum payload length for WebSocket control frames (ping, pong, close).

pub const MessageWriter = @import("message_writer.zig").ServerMessageWriter

src

Fragmented message writer that leaves each emitted frame unmasked.

pub const Parser = parse.ServerParser

src

Frame parser for masked frames received by a server.

pub const FrameHandler = parse.ServerFrameHandler

src

Message-oriented parser for server connections.

Functions

pub fn parseClosePayload(payload: []const u8) ClosePayloadError!?ClosePayload

src

Parse and validate a close frame payload.

pub fn computeAcceptKey(key: []const u8, out: *AcceptKey) []const u8

src

Compute the Sec-WebSocket-Accept header value for an upgrade response.

pub fn validateUpgradeRequest(request: UpgradeRequest) UpgradeError!void

src

Validate the WebSocket-specific headers from an upgrade request.

pub fn writeFrame( writer: *Io.Writer, payload: []const u8, options: WriteFrameOptions, ) Io.Writer.Error!void

src

Write a complete frame (header + payload) to writer. The caller is responsible for flushing when ready — this allows batching multiple frames before a single flush.

pub fn writeClose(writer: *Io.Writer, code: CloseCode) Io.Writer.Error!void

src

Write a close frame with a status code (no reason text).

pub fn writePing(writer: *Io.Writer, payload: []const u8) Io.Writer.Error!void

src

Write a ping frame with an optional payload.

pub fn writePong(writer: *Io.Writer, payload: []const u8) Io.Writer.Error!void

src

Write a pong frame with the given payload (typically echoed from a ping).

Types

enum CloseCode

src
pub const CloseCode = close.CloseCode

Close status codes defined by RFC 6455 Section 7.4.

Fields

normal normal = 1000
going_away going_away = 1001
protocol_error protocol_error = 1002
unsupported_data unsupported_data = 1003
no_status no_status = 1005
abnormal abnormal = 1006
invalid_payload invalid_payload = 1007
policy_violation policy_violation = 1008
too_big too_big = 1009
mandatory_extension mandatory_extension = 1010
internal_error internal_error = 1011
tls_handshake tls_handshake = 1015
_ _

Functions

pub fn isValid(self: CloseCode) bool

src

RFC 6455 Section 7.4: validate whether a close code may appear on the wire. Codes 1005, 1006, and 1015 are reserved for local use and MUST NOT be sent.

pub fn toBytes(self: CloseCode) [2]u8

src

Encode the status code as two big-endian bytes for a close frame payload.

struct ClosePayload

src
pub const ClosePayload = close.ClosePayload

Parsed close frame payload containing the status code and optional reason bytes.

Fields

code: CloseCode
reason: []const u8

union Extension

src
pub const Extension = @import("extension.zig").Extension

Supported negotiated WebSocket extensions.

Fields

permessage_deflate: DeflateConfig

Constants

pub const max_header_len = 256

src

pub const HeaderBuffer = frame.BoundedBuffer(max_header_len)

src

Functions

pub fn claimedRsv(self: Extension) RsvBits

src

pub fn allowedRsv(ext: ?Extension) RsvBits

src

pub fn writeHeader(self: Extension, buf: *HeaderBuffer) void

src

Types

struct DeflateConfig

src

Fields

Constants

pub const init: DeflateConfig = …

src
value
.{
            .server_no_context_takeover = false,
            .client_no_context_takeover = false,
            .server_max_window_bits = .@"15",
            .client_max_window_bits = .@"15",
        }

enum WindowBits

src
pub const WindowBits = enum(u4)

Fields

@"8" @"8" = 8
@"9" @"9" = 9
@"10" @"10" = 10
@"11" @"11" = 11
@"12" @"12" = 12
@"13" @"13" = 13
@"14" @"14" = 14
@"15" @"15" = 15

enum Opcode

src
pub const Opcode = frame.Opcode

WebSocket frame opcode.

Fields

continuation continuation = 0x0
text text = 0x1
binary binary = 0x2
close close = 0x8
ping ping = 0x9
pong pong = 0xA
_ _

Functions

pub fn isControl(self: Opcode) bool

src

Returns true for opcodes 0x8–0xF (control frames: close, ping, pong, and reserved).

pub fn isReserved(self: Opcode) bool

src

Returns true for opcodes not defined by RFC 6455 (i.e. not continuation, text, binary, close, ping, or pong).

struct FrameHeader

src
pub const FrameHeader = frame.FrameHeader

Decoded frame header and its serialized buffer helper.

Fields

fin: bool = true

True if this is the final fragment in a message.

rsv: RsvBits = .empty

Reserved bits for extensions.

opcode: Opcode

The frame opcode.

payload_len: u64

Length of the payload data in bytes.

mask_key: ?MaskKey = null

The masking key, or null for unmasked frames.

Functions

pub fn encodedLen(self: FrameHeader) usize

src

Returns the total wire size of this header (2..14 bytes).

Types

struct Buffer

src

A serialized frame header ready to write to the wire.

Fields

buf: HeaderBuffer

Functions

pub fn init(header: FrameHeader) Buffer

src

Serialize a FrameHeader into its wire representation.

pub fn constSlice(self: *const Buffer) []const u8

src

Returns the serialized header bytes.

pub fn write(self: *const Buffer, writer: *Io.Writer) Io.Writer.Error!void

src

Writes the serialized header to writer.

struct Mask

src
pub const Mask = frame.Mask

Stateful XOR mask for masking and unmasking payload bytes.

Fields

key: MaskKey
offset: u2

Constants

pub const none: Mask = .{ .key = @splat(0), .offset = 0 }

src

A no-op mask (all-zero key). XOR with zero leaves data unchanged.

Functions

pub fn init(key: MaskKey) Mask

src

Create a mask from key with the offset starting at zero.

pub fn apply(self: *Mask, payload: []u8) void

src

Apply the mask to payload in-place, advancing the internal offset. If the key is all zeros (unmasked), the XOR is a no-op.

struct RsvBits

src
pub const RsvBits = frame.RsvBits

RSV bitset from the frame header.

Fields

rsv3: bool = false
rsv2: bool = false
rsv1: bool = false

Constants

pub const empty: RsvBits = .{}

src

All bits unset (the default for frames without extensions).

pub const permessage_deflate: RsvBits = .{ .rsv1 = true }

src

RSV1 set, as required by permessage-deflate (RFC 7692 §6.1).

Functions

pub fn areSet(self: RsvBits) bool

src

Returns true if any RSV bit is set.

pub fn hasDisallowed(self: RsvBits, allowed: RsvBits) bool

src

Returns true if any bit is set that is not in allowed.

union Event

src
pub const Event = frame.Event

Streaming parser event.

Fields

frame_header: FrameHeader

A complete frame header has been parsed.

payload: []u8

A chunk of payload data (a slice into the caller's input buffer, already unmasked).

frame_end frame_end

The current frame is complete. The parser is ready for the next frame.

need_more need_more

More input is needed before the next event can be produced.

struct WriteFrameOptions

src
pub const WriteFrameOptions = frame.WriteFrameOptions

Options for frame-writing helpers.

Fields

opcode: Opcode

Frame opcode. Only non-reserved opcodes are valid.

compressed: bool = false

Set when the payload has been compressed with permessage-deflate. Controls the RSV1 bit on the first frame.

struct UpgradeRequest

src
pub const UpgradeRequest = handshake.UpgradeRequest

WebSocket-specific fields extracted from an HTTP upgrade request.

Fields

key: []const u8

Sec-WebSocket-Key header value.

version: []const u8

Sec-WebSocket-Version header value.

struct UpgradeResponse

src
pub const UpgradeResponse = handshake.UpgradeResponse

Prebuilt HTTP 101 response for a successful WebSocket upgrade.

Fields

buf: Buffer

Constants

pub const max_len = …

src
value
prefix.len + 28 + 2 + // accept + \r\n
        ext_header_prefix.len + Extension.max_header_len + 2 + // extension header + \r\n
        2

Maximum byte length of the complete HTTP response.

Functions

pub fn init(options: InitOptions) UpgradeResponse

src

Build the response from the client's Sec-WebSocket-Key header value and an optional negotiated extension.

pub fn constSlice(self: *const UpgradeResponse) []const u8

src

Returns the complete HTTP response bytes.

pub fn write(self: *const UpgradeResponse, writer: *Io.Writer) Io.Writer.Error!void

src

Writes the complete HTTP response to writer.

Types

struct InitOptions

src

Fields

key: []const u8
extension: ?Extension = null

struct Options

src
pub const Options = parse.Options

Parser options shared by both roles.

Fields

extension: ?Extension = null

Negotiated extension that is allowed to claim RSV bits during parsing.

struct MessageValidator

src
pub const MessageValidator = parse.MessageValidator

Fragmentation validator shared by both roles.

Fields

state: State

Constants

pub const init: MessageValidator = .{ .state = .idle }

src

Initial state, ready for the first data frame.

pub const ValidationError = …

src
value
error{
        /// Continuation frame received with no message in progress.
        UnexpectedContinuation,
        /// Text/binary frame received while a fragmented message is in progress.
        ExpectedContinuation,
    }

Errors returned by check for fragmentation protocol violations.

Functions

pub fn check(self: *MessageValidator, header: FrameHeader) ValidationError!void

src

Validate a frame header against fragmentation rules. Control frames are always allowed (they can be interleaved mid-fragment).