std.websocket
API summary (generated from the Beans source by npm run coverage): 13 package functions · 4 types · 7 static methods · 20 instance methods · 4 public fields · 5 enum variants.
std.websocket speaks RFC 6455 on top of std.http’s
upgrade handshake, with the framing done by wslay. The source is
stdlib/std/websocket/websocket.b.
import std.websocketRules that shape the package
Section titled “Rules that shape the package”receivereturns complete messages. The package handles fragmentation, continuation frames, and interleaved control frames.- Text means valid UTF-8, checked on the assembled message rather than per frame, because a code point may straddle a fragment boundary. A text message that is not well-formed UTF-8 is a protocol error.
- Pings are answered automatically. The library sends a pong before
reporting the
pingto your loop. Received pings are still reported, so callers can count them. - Close is a handshake, not a hangup.
closesends the close frame and waits, bounded, for the peer’s. A protocol violation sends the close frame the RFC requires and then closes the TCP connection immediately, as section 7.1.1 demands. - A message limit is part of the connection.
max_messagebounds an assembled message; crossing it is kindtoo_large. A peer cannot make a server allocate without limit by fragmenting forever.
Error kinds you may see: protocol (a framing or UTF-8 violation),
too_large, handshake (the upgrade was refused or the accept value did not
match), eof (the connection ended without a close frame), closed.
Module functions
Section titled “Module functions”pub fn available() -> boolpub fn accept_for_key(key: string) -> Result<string>pub fn upgrade_websocket<T implements net.ByteStream>(move stream: T, host: string, port: int, target: string, compress: bool = false) -> Result<WebSocketTransport<T>>pub fn wrap_websocket<T implements net.ByteStream>(move stream: T, server: bool, max_message: int = 8388608, agreed: Option<Deflate> = none) -> Result<WebSocketTransport<T>>pub fn accept_websocket<T implements net.ByteStream>(move stream: T, request: http.Request, max_message: int = 8388608, compress: bool = false, prefer: Option<Deflate> = none) -> Result<WebSocketTransport<T>>available reports whether the native framing bridge is present.
accept_for_key computes the Sec-WebSocket-Accept value from the client’s
Sec-WebSocket-Key: base64 of SHA-1 over the key and one fixed UUID.
The three generic helpers upgrade, wrap, or accept any owned net.ByteStream.
Use them for TLS. The static methods below expose the same operations.
Compression
Section titled “Compression”permessage-deflate (RFC 7692) is negotiated in the handshake, not configured
afterwards. Pass compress: true and the client offers it; a server that is
given compress: true accepts an offer it can honour.
pub struct Deflate { pub server_no_context_takeover: bool pub client_no_context_takeover: bool pub server_max_window_bits: int pub client_max_window_bits: int}server always names the server-to-client direction and client the
client-to-server one, whichever end you are — the names come from the wire,
not from your role. A no_context_takeover flag means that direction starts a
fresh DEFLATE context for every message, which costs ratio and bounds memory. A
max_window_bits is that direction’s LZ77 window, always 9 to 15.
The same struct also spells a server’s preference, which is what accept and
negotiate_deflate take as prefer. Read that way a true flag asks for a
parameter and a false one has no opinion, and a max_window_bits is a
ceiling where 15 means no opinion — because a preference may only ever
narrow an offer, never widen one.
pub fn negotiate_deflate(headers: http.Headers, prefer: Option<Deflate> = none) -> Option<Deflate>pub fn deflate_offer() -> stringpub fn deflate_agreement(agreed: Deflate) -> stringpub fn accept_deflate_response(value: string) -> Result<Deflate>These four are the handshake itself, for a server or client that writes its own
rather than using accept and upgrade:
negotiate_deflatereads a request’sSec-WebSocket-Extensionsoffers and answers the first one it can agree to, narrowed byprefer.nonemeans no compression — which is always a valid outcome, never an error.deflate_offeris the header value a client sends.deflate_agreementis the header value a server sends back for what it agreed to. Parameters at their defaults are left out.accept_deflate_responsereads a server’s answer on the client side. It refuses an answer naming more than one extension, or one the client did not offer.
Message
Section titled “Message”What arrived. text and binary are whole messages; ping and pong are the
control frames; closed carries the peer’s code and reason, after which the
connection is done.
pub enum Message { text(body: string) binary(body: Bytes) ping(body: Bytes) pong(body: Bytes) closed(code: int, reason: string)}WebSocketTransport
Section titled “WebSocketTransport”A move-only WebSocket over any owned byte stream:
pub unique class WebSocketTransport<T implements net.ByteStream> implements Send
pub static fn upgrade(move socket: T, host: string, port: int, target: string, compress: bool = false) -> Result<WebSocketTransport<T>>pub static fn wrap(move stream: T, server: bool, max_message: int = 8388608, agreed: Option<Deflate> = none) -> Result<WebSocketTransport<T>>pub static fn accept(move stream: T, request: http.Request, max_message: int = 8388608, compress: bool = false, prefer: Option<Deflate> = none) -> Result<WebSocketTransport<T>>pub fn receive() -> Result<Option<Message>>pub fn send_text(body: string) -> Result<bool>pub fn send_binary(body: Bytes) -> Result<bool>pub fn ping(body: Bytes) -> Result<bool>pub fn pong(body: Bytes) -> Result<bool>pub fn close(code: int, reason: string) -> Result<bool>pub fn peer_close_code() -> intpub fn is_open() -> boolpub fn deflate() -> Option<Deflate>pub fn poll_handle() -> intupgrade writes and verifies the client-side HTTP handshake over a connected
stream. accept writes the server-side 101 response for a request already
parsed by std.http. wrap takes a stream whose handshake is already done.
deflate answers the compression in force, or none on a connection that
negotiated none. See Compression.
Connection
Section titled “Connection”Connection is the raw-TCP wrapper. It owns the socket, implements Send, and
has the same instance methods as WebSocketTransport.
pub unique class Connection implements Send
pub static fn connect(host: string, port: int, target: string, compress: bool = false) -> Result<Connection>pub static fn connect_timeout(host: string, port: int, target: string, ms: int, compress: bool = false) -> Result<Connection>pub static fn accept(move stream: net.TcpStream, request: http.Request, max_message: int = 8388608, compress: bool = false, prefer: Option<Deflate> = none) -> Result<Connection>pub static fn wrap(move stream: net.TcpStream, server: bool, max_message: int = 8388608, agreed: Option<Deflate> = none) -> Result<Connection>pub fn receive() -> Result<Option<Message>>pub fn send_text(body: string) -> Result<bool>pub fn send_binary(body: Bytes) -> Result<bool>pub fn ping(body: Bytes) -> Result<bool>pub fn pong(body: Bytes) -> Result<bool>pub fn close(code: int, reason: string) -> Result<bool>pub fn peer_close_code() -> intpub fn is_open() -> boolpub fn deflate() -> Option<Deflate>pub fn poll_handle() -> intconnect does the TCP connect, the HTTP upgrade and the accept-value check.
target is the request target ("/chat"), not a whole URL — the host and port
are already decided. receive returns ok(none) when the close handshake has
finished; peer_close_code() is the code the peer sent, or 0.
accept completes a server-side upgrade for a request std.http already parsed
and writes the 101 response itself, so the caller hands over a socket that has
not been answered yet. wrap takes an already-upgraded socket, for a caller who
ran the handshake themselves.
poll_handle returns the borrowed transport handle for a readiness poller.
A client
Section titled “A client”let socket: websocket.Connection = websocket.Connection.connect("127.0.0.1", port, "/chat")?socket.send_text("hello")?match socket.receive()? { some(message) => { match message { text(body) => { io.println(body) } binary(body) => {} ping(body) => {} pong(body) => {} closed(code, reason) => {} } } none => {}}socket.close(1000, "done")?A server
Section titled “A server”The upgrade is ordinary HTTP, so std.http parses it and this package takes the
socket from there — which is why the handshake gets the same strict parser every
other HTTP message gets.
let parser: http.RequestParser = new http.RequestParser()// ... feed until a head arrives ...let live: websocket.Connection = websocket.Connection.accept(move stream, request)?Secure WebSocket
Section titled “Secure WebSocket”std.websocket_tls is a separate package so a plain ws-only binary links no
TLS backend. Import it for wss.
import std.websocket_tls
pub fn connect(host: string, port: int, target: string, ms: int = 30000, compress: bool = false) -> Result<websocket.WebSocketTransport<tls.TlsStream>>pub fn connect_with_roots(address: string, server_name: string, port: int, target: string, extra_roots: Bytes, ms: int = 30000, compress: bool = false) -> Result<websocket.WebSocketTransport<tls.TlsStream>>pub fn wrap(move stream: tls.TlsStream, server: bool, max_message: int = 8388608, agreed: Option<websocket.Deflate> = none) -> Result<websocket.WebSocketTransport<tls.TlsStream>>pub fn accept(move stream: tls.TlsStream, request: http.Request, max_message: int = 8388608, compress: bool = false, prefer: Option<websocket.Deflate> = none) -> Result<websocket.WebSocketTransport<tls.TlsStream>>connectdoes the TLS handshake and then the ordinary WebSocket HTTP upgrade againsttarget(the request path).msis the connect timeout.connect_with_rootssplits what to dial (address) from what to verify and send as SNI (server_name), and adds trust anchors in PEM form.wrapruns the protocol over atls.TlsStreamyou already hold;acceptis the server side, completing the upgrade for ahttp.Requestyou have already read.max_messagecaps a single message, defaulting to 8 MiB.
What comes back is the ordinary WebSocketTransport, so
WebSocketTransport and Connection above
apply unchanged.
Conformance
Section titled “Conformance”The framing uses wslay and is tested with the Autobahn TestSuite, run in containers against both an echo server and an echo client. The test gate requires no failed cases and no failed close-behavior checks.