CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
README.md549 linesDownload Raw Back to ws
1# ws: a Node.js WebSocket library2 3[![Version npm](https://img.shields.io/npm/v/ws.svg?logo=npm)](https://www.npmjs.com/package/ws)4[![CI](https://img.shields.io/github/actions/workflow/status/websockets/ws/ci.yml?branch=master&label=CI&logo=github)](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)5[![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg?logo=coveralls)](https://coveralls.io/github/websockets/ws)6 7ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and8server implementation.9 10Passes the quite extensive Autobahn test suite: [server][server-report],11[client][client-report].12 13**Note**: This module does not work in the browser. The client in the docs is a14reference to a backend with the role of a client in the WebSocket communication.15Browser clients must use the native16[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)17object. To make the same code work seamlessly on Node.js and the browser, you18can use one of the many wrappers available on npm, like19[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).20 21## Table of Contents22 23- [Protocol support](#protocol-support)24- [Installing](#installing)25  - [Opt-in for performance](#opt-in-for-performance)26    - [Legacy opt-in for performance](#legacy-opt-in-for-performance)27- [API docs](#api-docs)28- [WebSocket compression](#websocket-compression)29- [Usage examples](#usage-examples)30  - [Sending and receiving text data](#sending-and-receiving-text-data)31  - [Sending binary data](#sending-binary-data)32  - [Simple server](#simple-server)33  - [External HTTP/S server](#external-https-server)34  - [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)35  - [Client authentication](#client-authentication)36  - [Server broadcast](#server-broadcast)37  - [Round-trip time](#round-trip-time)38  - [Use the Node.js streams API](#use-the-nodejs-streams-api)39  - [Other examples](#other-examples)40- [FAQ](#faq)41  - [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)42  - [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)43  - [How to connect via a proxy?](#how-to-connect-via-a-proxy)44- [Changelog](#changelog)45- [License](#license)46 47## Protocol support48 49- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)50- **HyBi drafts 13-17** (Current default, alternatively option51  `protocolVersion: 13`)52 53## Installing54 55```56npm install ws57```58 59### Opt-in for performance60 61[bufferutil][] is an optional module that can be installed alongside the ws62module:63 64```65npm install --save-optional bufferutil66```67 68This is a binary addon that improves the performance of certain operations such69as masking and unmasking the data payload of the WebSocket frames. Prebuilt70binaries are available for the most popular platforms, so you don't necessarily71need to have a C++ compiler installed on your machine.72 73To force ws to not use bufferutil, use the74[`WS_NO_BUFFER_UTIL`](./doc/ws.md#ws_no_buffer_util) environment variable. This75can be useful to enhance security in systems where a user can put a package in76the package search path of an application of another user, due to how the77Node.js resolver algorithm works.78 79#### Legacy opt-in for performance80 81If you are running on an old version of Node.js (prior to v18.14.0), ws also82supports the [utf-8-validate][] module:83 84```85npm install --save-optional utf-8-validate86```87 88This contains a binary polyfill for [`buffer.isUtf8()`][].89 90To force ws not to use utf-8-validate, use the91[`WS_NO_UTF_8_VALIDATE`](./doc/ws.md#ws_no_utf_8_validate) environment variable.92 93## API docs94 95See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and96utility functions.97 98## WebSocket compression99 100ws supports the [permessage-deflate extension][permessage-deflate] which enables101the client and server to negotiate a compression algorithm and its parameters,102and then selectively apply it to the data payloads of each WebSocket message.103 104The extension is disabled by default on the server and enabled by default on the105client. It adds a significant overhead in terms of performance and memory106consumption so we suggest to enable it only if it is really needed.107 108Note that Node.js has a variety of issues with high-performance compression,109where increased concurrency, especially on Linux, can lead to [catastrophic110memory fragmentation][node-zlib-bug] and slow performance. If you intend to use111permessage-deflate in production, it is worthwhile to set up a test112representative of your workload and ensure Node.js/zlib will handle it with113acceptable performance and memory usage.114 115Tuning of permessage-deflate can be done via the options defined below. You can116also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly117into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].118 119See [the docs][ws-server-options] for more options.120 121```js122import WebSocket, { WebSocketServer } from 'ws';123 124const wss = new WebSocketServer({125  port: 8080,126  perMessageDeflate: {127    zlibDeflateOptions: {128      // See zlib defaults.129      chunkSize: 1024,130      memLevel: 7,131      level: 3132    },133    zlibInflateOptions: {134      chunkSize: 10 * 1024135    },136    // Other options settable:137    clientNoContextTakeover: true, // Defaults to negotiated value.138    serverNoContextTakeover: true, // Defaults to negotiated value.139    serverMaxWindowBits: 10, // Defaults to negotiated value.140    // Below options specified as default values.141    concurrencyLimit: 10, // Limits zlib concurrency for perf.142    threshold: 1024 // Size (in bytes) below which messages143    // should not be compressed if context takeover is disabled.144  }145});146```147 148The client will only use the extension if it is supported and enabled on the149server. To always disable the extension on the client, set the150`perMessageDeflate` option to `false`.151 152```js153import WebSocket from 'ws';154 155const ws = new WebSocket('ws://www.host.com/path', {156  perMessageDeflate: false157});158```159 160## Usage examples161 162### Sending and receiving text data163 164```js165import WebSocket from 'ws';166 167const ws = new WebSocket('ws://www.host.com/path');168 169ws.on('error', console.error);170 171ws.on('open', function open() {172  ws.send('something');173});174 175ws.on('message', function message(data) {176  console.log('received: %s', data);177});178```179 180### Sending binary data181 182```js183import WebSocket from 'ws';184 185const ws = new WebSocket('ws://www.host.com/path');186 187ws.on('error', console.error);188 189ws.on('open', function open() {190  const array = new Float32Array(5);191 192  for (var i = 0; i < array.length; ++i) {193    array[i] = i / 2;194  }195 196  ws.send(array);197});198```199 200### Simple server201 202```js203import { WebSocketServer } from 'ws';204 205const wss = new WebSocketServer({ port: 8080 });206 207wss.on('connection', function connection(ws) {208  ws.on('error', console.error);209 210  ws.on('message', function message(data) {211    console.log('received: %s', data);212  });213 214  ws.send('something');215});216```217 218### External HTTP/S server219 220```js221import { createServer } from 'https';222import { readFileSync } from 'fs';223import { WebSocketServer } from 'ws';224 225const server = createServer({226  cert: readFileSync('/path/to/cert.pem'),227  key: readFileSync('/path/to/key.pem')228});229const wss = new WebSocketServer({ server });230 231wss.on('connection', function connection(ws) {232  ws.on('error', console.error);233 234  ws.on('message', function message(data) {235    console.log('received: %s', data);236  });237 238  ws.send('something');239});240 241server.listen(8080);242```243 244### Multiple servers sharing a single HTTP/S server245 246```js247import { createServer } from 'http';248import { WebSocketServer } from 'ws';249 250const server = createServer();251const wss1 = new WebSocketServer({ noServer: true });252const wss2 = new WebSocketServer({ noServer: true });253 254wss1.on('connection', function connection(ws) {255  ws.on('error', console.error);256 257  // ...258});259 260wss2.on('connection', function connection(ws) {261  ws.on('error', console.error);262 263  // ...264});265 266server.on('upgrade', function upgrade(request, socket, head) {267  const { pathname } = new URL(request.url, 'wss://base.url');268 269  if (pathname === '/foo') {270    wss1.handleUpgrade(request, socket, head, function done(ws) {271      wss1.emit('connection', ws, request);272    });273  } else if (pathname === '/bar') {274    wss2.handleUpgrade(request, socket, head, function done(ws) {275      wss2.emit('connection', ws, request);276    });277  } else {278    socket.destroy();279  }280});281 282server.listen(8080);283```284 285### Client authentication286 287```js288import { createServer } from 'http';289import { WebSocketServer } from 'ws';290 291function onSocketError(err) {292  console.error(err);293}294 295const server = createServer();296const wss = new WebSocketServer({ noServer: true });297 298wss.on('connection', function connection(ws, request, client) {299  ws.on('error', console.error);300 301  ws.on('message', function message(data) {302    console.log(`Received message ${data} from user ${client}`);303  });304});305 306server.on('upgrade', function upgrade(request, socket, head) {307  socket.on('error', onSocketError);308 309  // This function is not defined on purpose. Implement it with your own logic.310  authenticate(request, function next(err, client) {311    if (err || !client) {312      socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');313      socket.destroy();314      return;315    }316 317    socket.removeListener('error', onSocketError);318 319    wss.handleUpgrade(request, socket, head, function done(ws) {320      wss.emit('connection', ws, request, client);321    });322  });323});324 325server.listen(8080);326```327 328Also see the provided [example][session-parse-example] using `express-session`.329 330### Server broadcast331 332A client WebSocket broadcasting to all connected WebSocket clients, including333itself.334 335```js336import WebSocket, { WebSocketServer } from 'ws';337 338const wss = new WebSocketServer({ port: 8080 });339 340wss.on('connection', function connection(ws) {341  ws.on('error', console.error);342 343  ws.on('message', function message(data, isBinary) {344    wss.clients.forEach(function each(client) {345      if (client.readyState === WebSocket.OPEN) {346        client.send(data, { binary: isBinary });347      }348    });349  });350});351```352 353A client WebSocket broadcasting to every other connected WebSocket clients,354excluding itself.355 356```js357import WebSocket, { WebSocketServer } from 'ws';358 359const wss = new WebSocketServer({ port: 8080 });360 361wss.on('connection', function connection(ws) {362  ws.on('error', console.error);363 364  ws.on('message', function message(data, isBinary) {365    wss.clients.forEach(function each(client) {366      if (client !== ws && client.readyState === WebSocket.OPEN) {367        client.send(data, { binary: isBinary });368      }369    });370  });371});372```373 374### Round-trip time375 376```js377import WebSocket from 'ws';378 379const ws = new WebSocket('wss://websocket-echo.com/');380 381ws.on('error', console.error);382 383ws.on('open', function open() {384  console.log('connected');385  ws.send(Date.now());386});387 388ws.on('close', function close() {389  console.log('disconnected');390});391 392ws.on('message', function message(data) {393  console.log(`Round-trip time: ${Date.now() - data} ms`);394 395  setTimeout(function timeout() {396    ws.send(Date.now());397  }, 500);398});399```400 401### Use the Node.js streams API402 403```js404import WebSocket, { createWebSocketStream } from 'ws';405 406const ws = new WebSocket('wss://websocket-echo.com/');407 408const duplex = createWebSocketStream(ws, { encoding: 'utf8' });409 410duplex.on('error', console.error);411 412duplex.pipe(process.stdout);413process.stdin.pipe(duplex);414```415 416### Other examples417 418For a full example with a browser client communicating with a ws server, see the419examples folder.420 421Otherwise, see the test cases.422 423## FAQ424 425### How to get the IP address of the client?426 427The remote IP address can be obtained from the raw socket.428 429```js430import { WebSocketServer } from 'ws';431 432const wss = new WebSocketServer({ port: 8080 });433 434wss.on('connection', function connection(ws, req) {435  const ip = req.socket.remoteAddress;436 437  ws.on('error', console.error);438});439```440 441When the server runs behind a proxy like NGINX, the de-facto standard is to use442the `X-Forwarded-For` header.443 444```js445wss.on('connection', function connection(ws, req) {446  const ip = req.headers['x-forwarded-for'].split(',')[0].trim();447 448  ws.on('error', console.error);449});450```451 452### How to detect and close broken connections?453 454Sometimes, the link between the server and the client can be interrupted in a455way that keeps both the server and the client unaware of the broken state of the456connection (e.g. when pulling the cord).457 458In these cases, ping messages can be used as a means to verify that the remote459endpoint is still responsive.460 461```js462import { WebSocketServer } from 'ws';463 464function heartbeat() {465  this.isAlive = true;466}467 468const wss = new WebSocketServer({ port: 8080 });469 470wss.on('connection', function connection(ws) {471  ws.isAlive = true;472  ws.on('error', console.error);473  ws.on('pong', heartbeat);474});475 476const interval = setInterval(function ping() {477  wss.clients.forEach(function each(ws) {478    if (ws.isAlive === false) return ws.terminate();479 480    ws.isAlive = false;481    ws.ping();482  });483}, 30000);484 485wss.on('close', function close() {486  clearInterval(interval);487});488```489 490Pong messages are automatically sent in response to ping messages as required by491the spec.492 493Just like the server example above, your clients might as well lose connection494without knowing it. You might want to add a ping listener on your clients to495prevent that. A simple implementation would be:496 497```js498import WebSocket from 'ws';499 500function heartbeat() {501  clearTimeout(this.pingTimeout);502 503  // Use `WebSocket#terminate()`, which immediately destroys the connection,504  // instead of `WebSocket#close()`, which waits for the close timer.505  // Delay should be equal to the interval at which your server506  // sends out pings plus a conservative assumption of the latency.507  this.pingTimeout = setTimeout(() => {508    this.terminate();509  }, 30000 + 1000);510}511 512const client = new WebSocket('wss://websocket-echo.com/');513 514client.on('error', console.error);515client.on('open', heartbeat);516client.on('ping', heartbeat);517client.on('close', function clear() {518  clearTimeout(this.pingTimeout);519});520```521 522### How to connect via a proxy?523 524Use a custom `http.Agent` implementation like [https-proxy-agent][] or525[socks-proxy-agent][].526 527## Changelog528 529We're using the GitHub [releases][changelog] for changelog entries.530 531## License532 533[MIT](LICENSE)534 535[`buffer.isutf8()`]: https://nodejs.org/api/buffer.html#bufferisutf8input536[bufferutil]: https://github.com/websockets/bufferutil537[changelog]: https://github.com/websockets/ws/releases538[client-report]: http://websockets.github.io/ws/autobahn/clients/539[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent540[node-zlib-bug]: https://github.com/nodejs/node/issues/8871541[node-zlib-deflaterawdocs]:542  https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options543[permessage-deflate]: https://tools.ietf.org/html/rfc7692544[server-report]: http://websockets.github.io/ws/autobahn/servers/545[session-parse-example]: ./examples/express-session-parse546[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent547[utf-8-validate]: https://github.com/websockets/utf-8-validate548[ws-server-options]: ./doc/ws.md#new-websocketserveroptions-callback549