High-level WebSocket API: a self-managed client facade plus the standalone server, both reachable through a single import.
import powpow/ws let client = newWsClient() client.onOpen do (ws: WsConnection): ws.sendMessage("hello") client.onMessage do (ws: WsConnection, kind: WsFrameKind, data: openArray[byte]): echo cast[string](@data) discard client.connect("wss://example.com/chat") client.run() # blocks; auto-reconnects per policy until close()
The client creates and owns its event loop internally (like newWsServer() without arguments): connect() arms everything, run() drives the loop on the calling thread. Failed handshakes and abnormal drops are retried automatically with exponential backoff and jitter; deliberate closes never trigger a reconnect.
Types
WsClient = ref object
- High-level WebSocket client. Owns its private event loop; user callbacks survive reconnects. One session at a time: while connected or mid-attempt, further connect calls return false.
WsGiveUpCb = proc (attempts: int) {.closure.}
- Fired once when the policy's maxRetries is exhausted; run() returns shortly afterwards.
WsReconnectPolicy = object maxRetries*: int ## Consecutive failed attempts allowed before giving ## up (-1 = unlimited). Successful connects reset ## the counter. backoffStartMs*: int ## Delay before the first retry (default 500 ms) backoffMaxMs*: int ## Ceiling for the exponential backoff (10 s) jitter*: float ## Fraction of randomization applied to each delay ## (0..0.95); avoids synchronized reconnect storms
- Reconnect behaviour for WsClient.
WsRetryCb = proc (attempt: int; delayMs: int) {.closure.}
- Fired before scheduling retry number attempt, which will run after roughly delayMs milliseconds.
Consts
DefaultWsReconnectPolicy = (maxRetries: -1, backoffStartMs: 500, backoffMaxMs: 10000, jitter: 0.25)
Procs
proc attemptCount(c: WsClient): int {.inline, ...raises: [], tags: [], forbids: [].}
- Total dial attempts since construction, including successful ones.
proc close(c: WsClient; code: int = 1000; reason: string = "") {. ...raises: [KeyError, OSError, Exception], tags: [RootEffect], forbids: [].}
- Deliberate shutdown: tears down the session (if any), cancels pending retries and stops the internal loop so a blocked run() returns. Deliberate closes never trigger a reconnect. This client cannot be reused afterwards; create a new one instead.
proc connect(c: WsClient; host: string; port: int; path: string = "/"; headers: openArray[(string, string)] = []; protocols: openArray[string] = []; pingIntervalMs: int = 0; idleTimeoutMs: int = 0): bool {....raises: [], tags: [TimeEffect], forbids: [].}
- Connect to host:port over plain TCP (use connect(url) for wss://).
proc connect(c: WsClient; url: string; headers: openArray[(string, string)] = []; protocols: openArray[string] = []; pingIntervalMs: int = 0; idleTimeoutMs: int = 0): bool {....raises: [WsError, SslError], tags: [TimeEffect], forbids: [].}
- Connect to a ws:// or wss:// URL. Raises WsError when the URL is malformed; wss:// targets get a verifying TLS context unless one was assigned earlier via setTlsContext. Returns false when this client is already connecting or connected; otherwise schedules the first attempt (it runs once run() is entered).
proc isConnected(c: WsClient): bool {.inline, ...raises: [], tags: [], forbids: [].}
- True when the current session completed the handshake and its transport is still open.
proc newWsClient(policy: WsReconnectPolicy = DefaultWsReconnectPolicy; maxFrameSize: int = DefaultMaxFrameSize; handshakeTimeoutMs: int = 10000): WsClient {....raises: [OSError], tags: [TimeEffect], forbids: [].}
- Create a WebSocket client that owns its event loop. Call the on* setters, then connect(), then run().
proc onGiveUp(c: WsClient; cb: WsGiveUpCb) {.inline, ...raises: [], tags: [], forbids: [].}
proc onMessage(c: WsClient; cb: WsMessageCb) {.inline, ...raises: [], tags: [], forbids: [].}
proc sendMessage(c: WsClient; data: seq[byte]) {.inline, ...raises: [KeyError, OSError, Exception], tags: [RootEffect], forbids: [].}
- Send a binary frame on the current session (no-op while disconnected).
proc sendMessage(c: WsClient; data: string) {.inline, ...raises: [KeyError, OSError, Exception], tags: [RootEffect], forbids: [].}
- Send a text frame on the current session (no-op while disconnected).
proc session(c: WsClient): WsConnection {.inline, ...raises: [], tags: [], forbids: [].}
- The current (or most recent) session. Prefer the ws argument handed to callbacks; this accessor exists for code outside them.
proc setTlsContext(c: WsClient; ctx: SslContext) {.inline, ...raises: [], tags: [], forbids: [].}
- Provide the TLS context used for subsequent wss:// connects (e.g. a non-verifying one against self-signed servers).
Exports
-
Loop, postToLoop, deferCall, pauseTimer, unregister, poll, unregisterFd, addInterval, TimerId, ==, modify, poll, ObserverCallback, FdWatcher, remove, EventType, close, cancelObserver, TimerCallback, Callback, register, init, resumeTimer, wake, TlsState, releaseBuf, PlatformEvent, newLoop, removeIdle, run, Platform, close, iouEnabled, timerCount, addIdle, monoMs, addCleanup, Observer, add, cancelTimer, runOnce, FdCallback, ensureCapacity, modify, acquireBuf, observe, stop, isRunning, addTimer, wrapTls, SslError, newClientTlsContext, isTlsActive, TlsRole, SslContext, newServerTlsContext, WsConnection, onClose, DefaultMaxFrameSize, computeAcceptKey, newWsServer, upgradeToWs, isEstablished, WsOpenCb, WsCloseCb, sendPing, WsError, handshakeCount, onMessage, connectWs, close, sendBinary, sendMessage, WsErrorCb, writeFrameMasked, sendPong, onOpen, newWsConnection, reset, writeFrame, newWsServer, sendHandshake, sendHandshakeRequest, parseWsFrames, buildHandshakeResponse, WsServer, sendText, onError, WsMessageCb, websocketUpgrade, listen, sendMessage, closeWs, getProtocol, parseWsUrl, WsFrameKind, start