net.websocket #
Websocket callbacks
Websocket event callbacks have result signatures, such as fn (mut client websocket.Client, message &websocket.Message) !. Callbacks that return void can also be registered: returning normally is treated as success. The C backend adapts their return value so the listener receives a valid successful result.
For callbacks registered with on_message_ref, the last voidptr argument is the original reference passed at registration. Cast it back to its concrete reference type in an unsafe block, and keep the referenced object alive while the callback can run. Such a reference can be forwarded as mut unsafe { &Context(ctx) } to a function taking mut context &Context.
A server's on_connect callback runs before its handshake response is written. Use on_attached or on_attached_ref to push frames after the handshake response has been written and the client's callbacks have been registered.
Explicit write batching
Opening handshakes reject empty input and malformed header lines with an error. Each nonempty header line needs a name followed by a colon; values may contain further colons.
client.write_messages([]websocket.Message) sends the supplied complete frames in order without waiting to collect more traffic. It uses one socket write and returns the wire byte count, including headers. Empty batches are a no-op. Control frames must be at most 125 bytes; continuation frames are not accepted. The same control payload limit applies to write, write_ptr, and close. For close, the two-byte status code leaves at most 123 bytes for the reason. An oversized reason sends no frame bytes and returns an error; the usual socket shutdown, state reset, and close event still run. Close frames are not accepted; call client.close(code, reason) for the closing handshake. The entire batch is validated before any frames are sent. Socket writes, including pongs from the reader, are serialized per connection. On a write error, close the connection: a prefix may already have been sent.
Incremental server frame decoding
ServerFrameDecoder.decode(mut input) processes masked client frames from a caller-owned buffer without performing socket I/O. It supports incomplete input, fragmented messages, interleaved controls, message limits, and protocol validation. See the decoder documentation for buffer ownership and usage.
Optional Linux reactor
new_reactor() serves already upgraded plaintext TCP sockets on an explicit worker thread using epoll and eventfd. It provides bounded input/output work, cross-thread send handles, backpressure, deadlines, and graceful shutdown. The existing Client and Server APIs keep their execution model. See the reactor documentation for ownership, callbacks, and limits. See text validation for the ASCII fast path and Unicode fallback.
Incoming 64-bit frame lengths must have the most significant bit clear and fit in V's int range. Oversized lengths are rejected before allocating the payload buffer.
Owning a client connection
Plain ws:// clients and accepted server connections do not create TLS contexts. Only wss:// clients allocate them, including the fresh context retained for reconnection after close.
client.connect_with_dialer(dialer) accepts a callback with the signature fn (address string) !&net.TcpConn. The callback returns a connected TCP socket and releases any failed candidates itself. After it returns successfully, the WebSocket client owns the socket, applies the configured timeouts and TLS, and performs the opening handshake. A custom dialer must provide any proxy tunnel itself; proxy_url is rejected on this path.
After joining all readers and writers, call client.dispose() to release the connection and TLS resources. It is safe before connecting, after a failed connection, or more than once. Disposal is terminal: create a new client for a later connection.
fn new_client #
fn new_client(address string, opt ClientOpt) !&Client
new_client instance a new websocket client
fn new_reactor #
Available on Linux only.
fn new_reactor(options ReactorOptions) !&Reactor
new_reactor allocates the poller. Call run exactly once, even if stop was requested before startup, then join the worker before discarding the reactor.
fn new_server #
fn new_server(family net.AddrFamily, port int, route string, opt ServerOpt) &Server
new_server instance a new websocket server on provided port and route
fn FrameDecodeKind.from #
fn FrameDecodeKind.from[W](input W) !FrameDecodeKind
from returns the FrameDecodeKind value of an integer or of a field name, or an error if there is none.
fn OPCode.from #
fn OPCode.from[W](input W) !OPCode
from returns the OPCode value of an integer or of a field name, or an error if there is none.
fn State.from #
fn State.from[W](input W) !State
from returns the State value of an integer or of a field name, or an error if there is none.
type AcceptClientFn #
type AcceptClientFn = fn (mut c ServerClient) !bool
type ServerAttachedFn #
type ServerAttachedFn = fn (mut c ServerClient) !
type ServerAttachedFn2 #
type ServerAttachedFn2 = fn (mut c ServerClient, v voidptr) !
type SocketCloseFn #
type SocketCloseFn = fn (mut c Client, code int, reason string) !
type SocketCloseFn2 #
type SocketCloseFn2 = fn (mut c Client, code int, reason string, v voidptr) !
type SocketErrorFn #
type SocketErrorFn = fn (mut c Client, err string) !
type SocketErrorFn2 #
type SocketErrorFn2 = fn (mut c Client, err string, v voidptr) !
type SocketMessageFn #
type SocketMessageFn = fn (mut c Client, msg &Message) !
type SocketMessageFn2 #
type SocketMessageFn2 = fn (mut c Client, msg &Message, v voidptr) !
type SocketOpenFn #
type SocketOpenFn = fn (mut c Client) !
type SocketOpenFn2 #
type SocketOpenFn2 = fn (mut c Client, v voidptr) !
fn (Uri) str #
fn (u Uri) str() string
str returns the string representation of the Uri
enum FrameDecodeKind #
enum FrameDecodeKind {
need_more
fragment
message
control
failure
}
FrameDecodeKind distinguishes an incomplete frame, an intermediate fragment, a complete message, a control frame, and a terminal protocol error.
enum OPCode #
enum OPCode {
continuation = 0x00
text_frame = 0x01
binary_frame = 0x02
close = 0x08
ping = 0x09
pong = 0x0A
}
OPCode represents the supported websocket frame types
enum State #
enum State {
connecting = 0
open
closing
closed
}
State represents the state of the websocket connection.
struct Client #
struct Client {
is_server bool
mut:
read_buffer [8192]u8
read_start int
read_end int
ssl_conn &ssl.SSLConn = unsafe { nil } // secure connection used when wss is used
proxy_url string
flags []Flag // flags used in handshake
fragments []Fragment // current fragments
message_callbacks []MessageEventHandler // all callbacks on_message
error_callbacks []ErrorEventHandler // all callbacks on_error
open_callbacks []OpenEventHandler // all callbacks on_open
close_callbacks []CloseEventHandler // all callbacks on_close
write_lock shared WriteLock
pub:
is_ssl bool // true if secure socket is used
uri Uri // uri of current connection
id string // unique id of client
read_timeout i64
write_timeout i64
pub mut:
header http.Header // headers that will be passed when connecting
conn &net.TcpConn = unsafe { nil } // underlying TCP socket connection
nonce_size int = 16 // size of nounce used for masking
panic_on_callback bool // set to true of callbacks can panic
client_state shared ClientState // current state of connection
// logger used to log messages
logger &log.Logger = default_logger
resource_name string // name of current resource
last_pong_ut i64 // last time in unix time we got a pong message
}
Client represents websocket client
fn (Client) on_message #
fn (mut ws Client) on_message(fun SocketMessageFn)
on_message registers a callback on new messages
fn (Client) on_message_ref #
fn (mut ws Client) on_message_ref(fun SocketMessageFn2, ref voidptr)
on_message_ref registers a callback on new messages and provides a reference object
fn (Client) on_error #
fn (mut ws Client) on_error(fun SocketErrorFn)
on_error registers a callback on errors
fn (Client) on_error_ref #
fn (mut ws Client) on_error_ref(fun SocketErrorFn2, ref voidptr)
on_error_ref registers a callback on errors and provides a reference object
fn (Client) on_open #
fn (mut ws Client) on_open(fun SocketOpenFn)
on_open registers a callback on successful opening the websocket
fn (Client) on_open_ref #
fn (mut ws Client) on_open_ref(fun SocketOpenFn2, ref voidptr)
on_open_ref registers a callback on successful opening the websocket and provides a reference object
fn (Client) on_close #
fn (mut ws Client) on_close(fun SocketCloseFn)
on_close registers a callback on closed socket
fn (Client) on_close_ref #
fn (mut ws Client) on_close_ref(fun SocketCloseFn2, ref voidptr)
on_close_ref registers a callback on closed socket and provides a reference object
fn (Client) validate_frame #
fn (mut ws Client) validate_frame(frame &Frame) !
validate_client validates client frame rules from RFC6455
fn (Client) read_next_message #
fn (mut ws Client) read_next_message() !Message
read_next_message reads 1 to n frames to compose a message
fn (Client) parse_frame_header #
fn (mut ws Client) parse_frame_header() !Frame
parse_frame_header parses next message by decoding the incoming frames
fn (Client) connect #
fn (mut ws Client) connect() !
connect connects to remote websocket server
fn (Client) connect_with_dialer #
fn (mut ws Client) connect_with_dialer(dialer fn (string) !&net.TcpConn) !
connect_with_dialer uses an owned TCP connection supplied by the caller. The dialer must return a connected socket and owns failed candidates. Once returned, this client owns cleanup. TLS and HTTP upgrade use the usual path. Custom dialers provide their own proxy tunneling; ClientOpt.proxy_url is not accepted here to avoid silently bypassing configured proxy behavior.
fn (Client) dispose #
fn (mut ws Client) dispose()
dispose ends a client generation after its reader and writers have joined. Unlike close (which supports reconnect), it also releases the freshly reset TLS context, or the initial context when dialing never produced Client.conn. It is idempotent; this client must not be reconnected after disposal.
fn (Client) listen #
fn (mut ws Client) listen() !
listen listens and processes incoming messages
fn (Client) ping #
fn (mut ws Client) ping() !
ping sends ping message to server
fn (Client) pong #
fn (mut ws Client) pong() !
pong sends pong message to server,
fn (Client) write_ptr #
fn (mut ws Client) write_ptr(bytes &u8, payload_len int, code OPCode) !int
write_ptr writes payload_len bytes from a pointer with a websocket message type. Control frame payloads larger than 125 bytes are rejected before socket access.
fn (Client) write #
fn (mut ws Client) write(bytes []u8, code OPCode) !int
write writes a byte array with a websocket message type to the socket. Control frame payloads may contain at most 125 bytes.
fn (Client) write_string #
fn (mut ws Client) write_string(str string) !int
write_str, writes a string with a websocket texttype to socket
fn (Client) close #
fn (mut ws Client) close(code int, message string) !
close closes the websocket connection. A positive code leaves at most 123 bytes for the reason. An oversized reason returns an error and still shuts down, resets, and emits the close event.
fn (Client) set_state #
fn (mut ws Client) set_state(state State)
set_state sets current state of the websocket connection
fn (Client) get_state #
fn (ws &Client) get_state() State
get_state return the current state of the websocket connection
fn (Client) reset_state #
fn (mut ws Client) reset_state() !
reset_state resets the websocket and initialize default settings
fn (Client) free #
fn (c &Client) free()
free handles manual free memory of Client struct
fn (Client) write_messages #
fn (mut ws Client) write_messages(messages []Message) !int
write_messages sends complete messages in order with one socket write. It does not wait for more messages or retain caller buffers after returning. The return value includes frame headers, as with write(). A failed write may have sent a prefix; close the connection instead of replaying the batch. Close frames are not accepted; use close() to perform the closing handshake.
struct ClientOpt #
struct ClientOpt {
pub:
read_timeout i64 = net.infinite_timeout
write_timeout i64 = 30 * time.second
logger &log.Logger = default_logger
proxy_url string // optional proxy URL used to open the websocket TCP tunnel
}
struct ClientState #
struct ClientState {
pub mut:
state State = .closed // current state of connection
}
struct DecodedFrame #
struct DecodedFrame {
pub:
kind FrameDecodeKind
consumed int
opcode OPCode = .continuation
payload []u8
close_code int
reason string
}
DecodedFrame borrows payload from the input or decoder until the next decode call. consumed bytes may be discarded only after the payload has been used. A failure carries a WebSocket close code and reason, not a consumed payload.
struct Message #
struct Message {
pub:
opcode OPCode // websocket frame type of this message
payload []u8 // payload of the message
}
Message represents a whole message combined from 1 to n frames
fn (Message) free #
fn (m &Message) free()
free handles manual free memory of Message struct
struct Reactor #
Available on Linux only.
struct Reactor {
options ReactorOptions
epoll_fd int
wake_fd int
mut:
owner_thread u64
serial u64
started u64
stop_requested u64
inbox shared ReactorInbox
payload_spare []u8
sockets map[u64]&ReactorSocket
dirty []u64
readable []u64
closed []ReactorClosed
stopping bool
}
Reactor owns all socket I/O on its run() thread. HTTP validates the upgrade and transfers TCP ownership with attach. Create several reactors for multiple workers. Existing Client and Server APIs keep their execution model.
fn (Reactor) attach #
Available on Linux only.
fn (mut r Reactor) attach(mut conn net.TcpConn, response string) !&ReactorClient
attach reserves capacity synchronously and transfers conn only on success. response is the HTTP 101 response, queued before any WebSocket output. Stop accessing conn after success. A later setup failure invokes on_close.
fn (Reactor) stop #
Available on Linux only.
fn (mut r Reactor) stop()
stop rejects new work immediately and requests a bounded graceful shutdown. It is idempotent and safe from a callback or another thread. Join run after it.
fn (Reactor) run #
Available on Linux only.
fn (mut r Reactor) run() !
run executes exactly once and releases all descriptors before returning. Monotonic IDs protect descriptor reuse. Reads, frames, and writes have per-turn budgets; partial sends resume at their byte offset. Deadline sweeps are 50 ms.
struct ReactorClient #
Available on Linux only.
struct ReactorClient {
pub:
id string
mut:
owner &Reactor = unsafe { nil }
key u64
state u64 // 0 pending, 1 open, 2 closing, 3 closed
overloaded u64
}
ReactorClient is a thread-safe handle. Successful methods accept commands, not delivery receipts. Every successful attach has exactly one on_close, including an asynchronous failure before on_open. Failed attach retains the caller's socket ownership and produces no callbacks.
fn (ReactorClient) is_open #
Available on Linux only.
fn (c &ReactorClient) is_open() bool
is_open reports whether the worker has attached the connection and no close has been requested. It is a snapshot, not a delivery guarantee.
fn (ReactorClient) write #
Available on Linux only.
fn (mut c ReactorClient) write(payload []u8, opcode OPCode) !int
write accepts a final text, binary, ping, or pong frame. Control payloads are limited to 125 bytes. Cross-thread payloads are copied before returning.
fn (ReactorClient) write_string #
Available on Linux only.
fn (mut c ReactorClient) write_string(text string) !int
write_string accepts a final text frame. Success means accepted, not delivered.
fn (ReactorClient) close #
Available on Linux only.
fn (mut c ReactorClient) close(code int, reason string) !
close starts a bounded closing handshake after accepted output. New sends fail once close is accepted. TCP closes after the peer reply or close_timeout.
fn (ReactorClient) set_read_timeout #
Available on Linux only.
fn (mut c ReactorClient) set_read_timeout(timeout time.Duration) !
set_read_timeout changes the connection's read inactivity deadline.
struct ReactorOptions #
Available on Linux only.
struct ReactorOptions {
pub:
max_message_bytes int = 16384
max_pending_messages int = 64
max_pending_bytes int = 2 * 1024 * 1024
max_connections int = 10000
max_commands int = 8192
max_command_bytes int = 16 * 1024 * 1024
frames_per_turn int = 256
read_timeout time.Duration = 5 * time.second
write_timeout time.Duration = 5 * time.second
close_timeout time.Duration = time.second
on_open fn (mut ReactorClient, voidptr) = reactor_no_open
on_message fn (mut ReactorClient, &Message, voidptr) = reactor_no_message
on_close fn (mut ReactorClient, int, string, voidptr) = reactor_no_close
user voidptr
}
ReactorOptions configures an optional Linux plaintext server transport. Callbacks run serially on the owning worker, must not block, and borrow each message payload only until on_message returns. Clone retained payloads.
struct Server #
struct Server {
mut:
logger &log.Logger = default_logger
ls &net.TcpListener = unsafe { nil } // listener used to get incoming connection to socket
accept_client_callbacks []AcceptClientFn // accept client callback functions
attached_callbacks []AttachedEventHandler
message_callbacks []MessageEventHandler // new message callback functions
close_callbacks []CloseEventHandler // close message callback functions
pub:
family net.AddrFamily = .ip
port int // port used as listen to incoming connections
is_ssl bool // true if secure connection (not supported yet on server)
pub mut:
server_state shared ServerState
}
Server represents a websocket server connection
fn (Server) on_connect #
fn (mut s Server) on_connect(fun AcceptClientFn) !
on_connect registers a callback when client connects to the server
fn (Server) on_message #
fn (mut s Server) on_message(fun SocketMessageFn)
on_message registers a callback on new messages
fn (Server) on_message_ref #
fn (mut s Server) on_message_ref(fun SocketMessageFn2, ref voidptr)
on_message_ref registers a callback on new messages and provides a reference object
fn (Server) on_close #
fn (mut s Server) on_close(fun SocketCloseFn)
on_close registers a callback on closed socket
fn (Server) on_close_ref #
fn (mut s Server) on_close_ref(fun SocketCloseFn2, ref voidptr)
on_close_ref registers a callback on closed socket and provides a reference object
fn (Server) on_attached #
fn (mut s Server) on_attached(fun ServerAttachedFn)
on_attached registers a callback after a client has been attached to the server, the handshake response has been written, and callbacks are set up, but before the blocking listen loop begins.
fn (Server) on_attached_ref #
fn (mut s Server) on_attached_ref(fun ServerAttachedFn2, ref voidptr)
on_attached_ref registers a callback after a client has been attached to the server and provides a reference object.
fn (Server) set_ping_interval #
fn (mut s Server) set_ping_interval(seconds int)
set_ping_interval sets the interval that the server will send ping messages to clients
fn (Server) get_ping_interval #
fn (mut s Server) get_ping_interval() int
get_ping_interval return the interval that the server will send ping messages to clients
fn (Server) listen #
fn (mut s Server) listen() !
listen start listen and process to incoming connections from websocket clients
fn (Server) handle_handshake #
fn (mut s Server) handle_handshake(mut conn net.TcpConn, key string) !&ServerClient
handle_handshake use an existing connection to respond to the handshake for a given key
fn (Server) set_state #
fn (mut s Server) set_state(state State)
set_state sets current state in a thread safe way
fn (Server) get_state #
fn (s &Server) get_state() State
get_state return current state in a thread safe way
fn (Server) free #
fn (mut s Server) free()
free manages manual free of memory for Server instance
struct ServerClient #
struct ServerClient {
pub:
resource_name string // resource that the client access
client_key string // unique key of client
pub mut:
server &Server = unsafe { nil }
client &Client = unsafe { nil }
}
ServerClient represents a connected client
struct ServerFrameDecoder #
struct ServerFrameDecoder {
pub:
max_message_bytes int = 16384
mut:
fragment []u8
fragment_opcode u8
fragment_done bool
text_state FrameUtf8
failure_code int
failure_reason string
}
ServerFrameDecoder incrementally decodes masked client frames without socket I/O. Keep incomplete input and append more bytes before retrying decode. One instance belongs to one connection and must not be shared concurrently.
fn (ServerFrameDecoder) decode #
fn (mut decoder ServerFrameDecoder) decode(mut input []u8) DecodedFrame
decode consumes at most one complete frame. It checks declared lengths before allocation, unmasks complete payloads in place, combines fragments, and checks UTF-8, close payloads, reserved bits/opcodes, and canonical lengths. Incomplete input is not modified. An error is terminal: create a new decoder to reset it.
struct ServerOpt #
struct ServerOpt {
pub:
logger &log.Logger = default_logger
}
struct ServerState #
struct ServerState {
mut:
ping_interval int = 30 // interval for sending ping to clients (seconds)
state State = .closed // current state of connection
pub mut:
clients map[string]&ServerClient // clients connected to this server
}
- README
- fn new_client
- fn new_reactor
- fn new_server
- fn FrameDecodeKind.from
- fn OPCode.from
- fn State.from
- type AcceptClientFn
- type ServerAttachedFn
- type ServerAttachedFn2
- type SocketCloseFn
- type SocketCloseFn2
- type SocketErrorFn
- type SocketErrorFn2
- type SocketMessageFn
- type SocketMessageFn2
- type SocketOpenFn
- type SocketOpenFn2
- type Uri
- enum FrameDecodeKind
- enum OPCode
- enum State
- struct Client
- fn on_message
- fn on_message_ref
- fn on_error
- fn on_error_ref
- fn on_open
- fn on_open_ref
- fn on_close
- fn on_close_ref
- fn validate_frame
- fn read_next_message
- fn parse_frame_header
- fn connect
- fn connect_with_dialer
- fn dispose
- fn listen
- fn ping
- fn pong
- fn write_ptr
- fn write
- fn write_string
- fn close
- fn set_state
- fn get_state
- fn reset_state
- fn free
- fn write_messages
- struct ClientOpt
- struct ClientState
- struct DecodedFrame
- struct Message
- struct Reactor
- struct ReactorClient
- struct ReactorOptions
- struct Server
- struct ServerClient
- struct ServerFrameDecoder
- struct ServerOpt
- struct ServerState