SPB Git forge

spb/admin-ka

Public
41commits 1branches 0releases
172.9 MBsize
maindefault branch
19 days agolast push
JavaScript 65.5% Python 17.8% CSS 13% HTML 3.7%
14.9 KB · 549 lines markdown
Rendered Raw Blame History
1# ws: a Node.js WebSocket library23[![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)67ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and8server implementation.910Passes the quite extensive Autobahn test suite: [server][server-report],11[client][client-report].1213**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).2021## Table of Contents2223- [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)4647## Protocol support4849- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)50- **HyBi drafts 13-17** (Current default, alternatively option51  `protocolVersion: 13`)5253## Installing5455```56npm install ws57```5859### Opt-in for performance6061[bufferutil][] is an optional module that can be installed alongside the ws62module:6364```65npm install --save-optional bufferutil66```6768This 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.7273To 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.7879#### Legacy opt-in for performance8081If you are running on an old version of Node.js (prior to v18.14.0), ws also82supports the [utf-8-validate][] module:8384```85npm install --save-optional utf-8-validate86```8788This contains a binary polyfill for [`buffer.isUtf8()`][].8990To 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.9293## API docs9495See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and96utility functions.9798## WebSocket compression99100ws 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.103104The 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.107108Note 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.114115Tuning 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].118119See [the docs][ws-server-options] for more options.120121```js122import WebSocket, { WebSocketServer } from 'ws';123124const 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```147148The 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`.151152```js153import WebSocket from 'ws';154155const ws = new WebSocket('ws://www.host.com/path', {156  perMessageDeflate: false157});158```159160## Usage examples161162### Sending and receiving text data163164```js165import WebSocket from 'ws';166167const ws = new WebSocket('ws://www.host.com/path');168169ws.on('error', console.error);170171ws.on('open', function open() {172  ws.send('something');173});174175ws.on('message', function message(data) {176  console.log('received: %s', data);177});178```179180### Sending binary data181182```js183import WebSocket from 'ws';184185const ws = new WebSocket('ws://www.host.com/path');186187ws.on('error', console.error);188189ws.on('open', function open() {190  const array = new Float32Array(5);191192  for (var i = 0; i < array.length; ++i) {193    array[i] = i / 2;194  }195196  ws.send(array);197});198```199200### Simple server201202```js203import { WebSocketServer } from 'ws';204205const wss = new WebSocketServer({ port: 8080 });206207wss.on('connection', function connection(ws) {208  ws.on('error', console.error);209210  ws.on('message', function message(data) {211    console.log('received: %s', data);212  });213214  ws.send('something');215});216```217218### External HTTP/S server219220```js221import { createServer } from 'https';222import { readFileSync } from 'fs';223import { WebSocketServer } from 'ws';224225const server = createServer({226  cert: readFileSync('/path/to/cert.pem'),227  key: readFileSync('/path/to/key.pem')228});229const wss = new WebSocketServer({ server });230231wss.on('connection', function connection(ws) {232  ws.on('error', console.error);233234  ws.on('message', function message(data) {235    console.log('received: %s', data);236  });237238  ws.send('something');239});240241server.listen(8080);242```243244### Multiple servers sharing a single HTTP/S server245246```js247import { createServer } from 'http';248import { WebSocketServer } from 'ws';249250const server = createServer();251const wss1 = new WebSocketServer({ noServer: true });252const wss2 = new WebSocketServer({ noServer: true });253254wss1.on('connection', function connection(ws) {255  ws.on('error', console.error);256257  // ...258});259260wss2.on('connection', function connection(ws) {261  ws.on('error', console.error);262263  // ...264});265266server.on('upgrade', function upgrade(request, socket, head) {267  const { pathname } = new URL(request.url, 'wss://base.url');268269  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});281282server.listen(8080);283```284285### Client authentication286287```js288import { createServer } from 'http';289import { WebSocketServer } from 'ws';290291function onSocketError(err) {292  console.error(err);293}294295const server = createServer();296const wss = new WebSocketServer({ noServer: true });297298wss.on('connection', function connection(ws, request, client) {299  ws.on('error', console.error);300301  ws.on('message', function message(data) {302    console.log(`Received message ${data} from user ${client}`);303  });304});305306server.on('upgrade', function upgrade(request, socket, head) {307  socket.on('error', onSocketError);308309  // 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    }316317    socket.removeListener('error', onSocketError);318319    wss.handleUpgrade(request, socket, head, function done(ws) {320      wss.emit('connection', ws, request, client);321    });322  });323});324325server.listen(8080);326```327328Also see the provided [example][session-parse-example] using `express-session`.329330### Server broadcast331332A client WebSocket broadcasting to all connected WebSocket clients, including333itself.334335```js336import WebSocket, { WebSocketServer } from 'ws';337338const wss = new WebSocketServer({ port: 8080 });339340wss.on('connection', function connection(ws) {341  ws.on('error', console.error);342343  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```352353A client WebSocket broadcasting to every other connected WebSocket clients,354excluding itself.355356```js357import WebSocket, { WebSocketServer } from 'ws';358359const wss = new WebSocketServer({ port: 8080 });360361wss.on('connection', function connection(ws) {362  ws.on('error', console.error);363364  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```373374### Round-trip time375376```js377import WebSocket from 'ws';378379const ws = new WebSocket('wss://websocket-echo.com/');380381ws.on('error', console.error);382383ws.on('open', function open() {384  console.log('connected');385  ws.send(Date.now());386});387388ws.on('close', function close() {389  console.log('disconnected');390});391392ws.on('message', function message(data) {393  console.log(`Round-trip time: ${Date.now() - data} ms`);394395  setTimeout(function timeout() {396    ws.send(Date.now());397  }, 500);398});399```400401### Use the Node.js streams API402403```js404import WebSocket, { createWebSocketStream } from 'ws';405406const ws = new WebSocket('wss://websocket-echo.com/');407408const duplex = createWebSocketStream(ws, { encoding: 'utf8' });409410duplex.on('error', console.error);411412duplex.pipe(process.stdout);413process.stdin.pipe(duplex);414```415416### Other examples417418For a full example with a browser client communicating with a ws server, see the419examples folder.420421Otherwise, see the test cases.422423## FAQ424425### How to get the IP address of the client?426427The remote IP address can be obtained from the raw socket.428429```js430import { WebSocketServer } from 'ws';431432const wss = new WebSocketServer({ port: 8080 });433434wss.on('connection', function connection(ws, req) {435  const ip = req.socket.remoteAddress;436437  ws.on('error', console.error);438});439```440441When the server runs behind a proxy like NGINX, the de-facto standard is to use442the `X-Forwarded-For` header.443444```js445wss.on('connection', function connection(ws, req) {446  const ip = req.headers['x-forwarded-for'].split(',')[0].trim();447448  ws.on('error', console.error);449});450```451452### How to detect and close broken connections?453454Sometimes, 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).457458In these cases, ping messages can be used as a means to verify that the remote459endpoint is still responsive.460461```js462import { WebSocketServer } from 'ws';463464function heartbeat() {465  this.isAlive = true;466}467468const wss = new WebSocketServer({ port: 8080 });469470wss.on('connection', function connection(ws) {471  ws.isAlive = true;472  ws.on('error', console.error);473  ws.on('pong', heartbeat);474});475476const interval = setInterval(function ping() {477  wss.clients.forEach(function each(ws) {478    if (ws.isAlive === false) return ws.terminate();479480    ws.isAlive = false;481    ws.ping();482  });483}, 30000);484485wss.on('close', function close() {486  clearInterval(interval);487});488```489490Pong messages are automatically sent in response to ping messages as required by491the spec.492493Just 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:496497```js498import WebSocket from 'ws';499500function heartbeat() {501  clearTimeout(this.pingTimeout);502503  // 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}511512const client = new WebSocket('wss://websocket-echo.com/');513514client.on('error', console.error);515client.on('open', heartbeat);516client.on('ping', heartbeat);517client.on('close', function clear() {518  clearTimeout(this.pingTimeout);519});520```521522### How to connect via a proxy?523524Use a custom `http.Agent` implementation like [https-proxy-agent][] or525[socks-proxy-agent][].526527## Changelog528529We're using the GitHub [releases][changelog] for changelog entries.530531## License532533[MIT](LICENSE)534535[`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