WebSocket Client

Open a WebSocket to somewhere else and hold it open. This is the outbound half: b.websocket accepts connections, b.wsClient makes them.

An outbound connection is a request this server makes on behalf of whoever asked for it, so the target is screened against server-side request forgery before the socket opens. One that resolves to a loopback, link-local or private address is refused unless the caller opts out, because a URL taken from a webhook registration or a tenant's configuration should not be able to reach the metadata service.

Everything a peer sends is bounded. A message, a single frame and the number of fragments a message may arrive in each have a ceiling, so a peer cannot grow this process's memory by never finishing a message.

The connection is kept alive with a ping and a pong deadline, so a peer that stops answering is treated as gone rather than left holding a socket. Reconnection backs off.

b.wsClient.connect(target, opts?) #

stable0.7.114soc2
{
  subprotocols:       string[],  // offered in Sec-WebSocket-Protocol
  headers:            object,    // sent with the upgrade request
  origin:             string,    // Origin header value
  tlsOpts:            object,    // TLS options for a wss target
  tlsOptsFor:         function,  // (attempt) → TLS options, per attempt
  urlFor:             function,  // (attempt) → target URL, per attempt
  handshakeGuid:      string,    // GUID mixed into Sec-WebSocket-Accept; default the RFC 6455 one
  allowInternal:      boolean,   // default false — waive the SSRF screen
  pingMs:             number,    // keep-alive interval
  pongMs:             number,    // deadline for a pong before the peer is gone
  handshakeTimeoutMs: number,    // ceiling on the upgrade
  maxMessageBytes:    number,    // ceiling on one reassembled message
  maxFrameBytes:      number,    // ceiling on one frame
  maxFragments:       number,    // fragments one message may arrive in
  permessageDeflate:  boolean,   // negotiate the compression extension
  reconnect:          object,    // backoff for reconnection
  parse:              string,    // "json" to parse each message
  parser:             function,  // (data, isBinary) → value
  audit:              boolean,   // default true
}

Answer the client for a WebSocket to target. It returns immediately, before the socket is open: the client is an EventEmitter carrying send(data, opts?), ping(payload?) and close(code?, reason?), and the connection is reported through its events.

It emits open, message with (data, isBinary), close with (code, reason), error, and reconnecting with { attempt, delayMs }. Attach an error listener before anything else: every connection failure arrives there rather than as a throw, and an error an EventEmitter has no listener for takes the process down.

The options are checked before the client is returned, so a malformed call throws instead of reporting on error: ws-client/bad-url-for and ws-client/bad-tls-opts-for when those are not functions, ws-client/bad-handshake-guid for an empty handshakeGuid, ws-client/bad-subprotocol for an entry of subprotocols that is not a non-empty string, and ws-client/bad-opt when pingMs, pongMs, maxMessageBytes, maxFrameBytes, maxFragments or handshakeTimeoutMs is not a positive integer.

The target is screened before the socket opens, and one resolving to an address the SSRF guard refuses raises error instead of connecting, naming the range it landed in. allowInternal: true waives that, which is for a target the operator wrote rather than one a request supplied.

maxMessageBytes, maxFrameBytes and maxFragments bound what a peer can make this process hold. pingMs and pongMs set the keep-alive and the deadline for an answer; a missed pong raises ws-client/pong-timeout, which is transient and so reconnects rather than giving up.

parse: "json" or a parser function turns each message into a value before message fires; a payload that does not parse raises ws-client/json-parse or ws-client/parser-failed on error rather than reaching a handler half-read.

urlFor and tlsOptsFor are called per attempt, so a reconnect can pick up a rotated token or a new endpoint instead of replaying the one the first attempt used.

A target whose scheme is not ws: or wss: throws ws-client/bad-url. A reconnect that is not false, null or an object throws ws-client/bad-reconnect, and one carrying an unknown key or a baseMs or maxMs that is not a positive integer throws ws-client/bad-reconnect-opt.

var b = require("@blamejs/core");
var ws = b.wsClient.connect("wss://feed.example/stream", {
  subprotocols: ["v1"], parse: "json",
});
ws.on("error",   function (err) { log(err); });
ws.on("message", function (msg) { handle(msg); });
ws.on("open",    function () { ws.send(JSON.stringify({ subscribe: "ticks" })); });

Last updated 2026-10-12T14:21:56.879Z by seeder.