README.md
  1  # ws: a Node.js WebSocket library
  2  
  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/workflow/status/websockets/ws/CI/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  
  7  ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
  8  server implementation.
  9  
 10  Passes 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 a
 14  reference to a back end with the role of a client in the WebSocket
 15  communication. Browser clients must use the native
 16  [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
 17  object. To make the same code work seamlessly on Node.js and the browser, you
 18  can use one of the many wrappers available on npm, like
 19  [isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
 20  
 21  ## Table of Contents
 22  
 23  - [Protocol support](#protocol-support)
 24  - [Installing](#installing)
 25    - [Opt-in for performance](#opt-in-for-performance)
 26  - [API docs](#api-docs)
 27  - [WebSocket compression](#websocket-compression)
 28  - [Usage examples](#usage-examples)
 29    - [Sending and receiving text data](#sending-and-receiving-text-data)
 30    - [Sending binary data](#sending-binary-data)
 31    - [Simple server](#simple-server)
 32    - [External HTTP/S server](#external-https-server)
 33    - [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
 34    - [Client authentication](#client-authentication)
 35    - [Server broadcast](#server-broadcast)
 36    - [echo.websocket.org demo](#echowebsocketorg-demo)
 37    - [Use the Node.js streams API](#use-the-nodejs-streams-api)
 38    - [Other examples](#other-examples)
 39  - [FAQ](#faq)
 40    - [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
 41    - [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
 42    - [How to connect via a proxy?](#how-to-connect-via-a-proxy)
 43  - [Changelog](#changelog)
 44  - [License](#license)
 45  
 46  ## Protocol support
 47  
 48  - **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
 49  - **HyBi drafts 13-17** (Current default, alternatively option
 50    `protocolVersion: 13`)
 51  
 52  ## Installing
 53  
 54  ```
 55  npm install ws
 56  ```
 57  
 58  ### Opt-in for performance
 59  
 60  There are 2 optional modules that can be installed along side with the ws
 61  module. These modules are binary addons which improve certain operations.
 62  Prebuilt binaries are available for the most popular platforms so you don't
 63  necessarily need to have a C++ compiler installed on your machine.
 64  
 65  - `npm install --save-optional bufferutil`: Allows to efficiently perform
 66    operations such as masking and unmasking the data payload of the WebSocket
 67    frames.
 68  - `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
 69    message contains valid UTF-8.
 70  
 71  ## API docs
 72  
 73  See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
 74  utility functions.
 75  
 76  ## WebSocket compression
 77  
 78  ws supports the [permessage-deflate extension][permessage-deflate] which enables
 79  the client and server to negotiate a compression algorithm and its parameters,
 80  and then selectively apply it to the data payloads of each WebSocket message.
 81  
 82  The extension is disabled by default on the server and enabled by default on the
 83  client. It adds a significant overhead in terms of performance and memory
 84  consumption so we suggest to enable it only if it is really needed.
 85  
 86  Note that Node.js has a variety of issues with high-performance compression,
 87  where increased concurrency, especially on Linux, can lead to [catastrophic
 88  memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
 89  permessage-deflate in production, it is worthwhile to set up a test
 90  representative of your workload and ensure Node.js/zlib will handle it with
 91  acceptable performance and memory usage.
 92  
 93  Tuning of permessage-deflate can be done via the options defined below. You can
 94  also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
 95  into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
 96  
 97  See [the docs][ws-server-options] for more options.
 98  
 99  ```js
100  const WebSocket = require('ws');
101  
102  const wss = new WebSocket.Server({
103    port: 8080,
104    perMessageDeflate: {
105      zlibDeflateOptions: {
106        // See zlib defaults.
107        chunkSize: 1024,
108        memLevel: 7,
109        level: 3
110      },
111      zlibInflateOptions: {
112        chunkSize: 10 * 1024
113      },
114      // Other options settable:
115      clientNoContextTakeover: true, // Defaults to negotiated value.
116      serverNoContextTakeover: true, // Defaults to negotiated value.
117      serverMaxWindowBits: 10, // Defaults to negotiated value.
118      // Below options specified as default values.
119      concurrencyLimit: 10, // Limits zlib concurrency for perf.
120      threshold: 1024 // Size (in bytes) below which messages
121      // should not be compressed.
122    }
123  });
124  ```
125  
126  The client will only use the extension if it is supported and enabled on the
127  server. To always disable the extension on the client set the
128  `perMessageDeflate` option to `false`.
129  
130  ```js
131  const WebSocket = require('ws');
132  
133  const ws = new WebSocket('ws://www.host.com/path', {
134    perMessageDeflate: false
135  });
136  ```
137  
138  ## Usage examples
139  
140  ### Sending and receiving text data
141  
142  ```js
143  const WebSocket = require('ws');
144  
145  const ws = new WebSocket('ws://www.host.com/path');
146  
147  ws.on('open', function open() {
148    ws.send('something');
149  });
150  
151  ws.on('message', function incoming(data) {
152    console.log(data);
153  });
154  ```
155  
156  ### Sending binary data
157  
158  ```js
159  const WebSocket = require('ws');
160  
161  const ws = new WebSocket('ws://www.host.com/path');
162  
163  ws.on('open', function open() {
164    const array = new Float32Array(5);
165  
166    for (var i = 0; i < array.length; ++i) {
167      array[i] = i / 2;
168    }
169  
170    ws.send(array);
171  });
172  ```
173  
174  ### Simple server
175  
176  ```js
177  const WebSocket = require('ws');
178  
179  const wss = new WebSocket.Server({ port: 8080 });
180  
181  wss.on('connection', function connection(ws) {
182    ws.on('message', function incoming(message) {
183      console.log('received: %s', message);
184    });
185  
186    ws.send('something');
187  });
188  ```
189  
190  ### External HTTP/S server
191  
192  ```js
193  const fs = require('fs');
194  const https = require('https');
195  const WebSocket = require('ws');
196  
197  const server = https.createServer({
198    cert: fs.readFileSync('/path/to/cert.pem'),
199    key: fs.readFileSync('/path/to/key.pem')
200  });
201  const wss = new WebSocket.Server({ server });
202  
203  wss.on('connection', function connection(ws) {
204    ws.on('message', function incoming(message) {
205      console.log('received: %s', message);
206    });
207  
208    ws.send('something');
209  });
210  
211  server.listen(8080);
212  ```
213  
214  ### Multiple servers sharing a single HTTP/S server
215  
216  ```js
217  const http = require('http');
218  const WebSocket = require('ws');
219  const url = require('url');
220  
221  const server = http.createServer();
222  const wss1 = new WebSocket.Server({ noServer: true });
223  const wss2 = new WebSocket.Server({ noServer: true });
224  
225  wss1.on('connection', function connection(ws) {
226    // ...
227  });
228  
229  wss2.on('connection', function connection(ws) {
230    // ...
231  });
232  
233  server.on('upgrade', function upgrade(request, socket, head) {
234    const pathname = url.parse(request.url).pathname;
235  
236    if (pathname === '/foo') {
237      wss1.handleUpgrade(request, socket, head, function done(ws) {
238        wss1.emit('connection', ws, request);
239      });
240    } else if (pathname === '/bar') {
241      wss2.handleUpgrade(request, socket, head, function done(ws) {
242        wss2.emit('connection', ws, request);
243      });
244    } else {
245      socket.destroy();
246    }
247  });
248  
249  server.listen(8080);
250  ```
251  
252  ### Client authentication
253  
254  ```js
255  const http = require('http');
256  const WebSocket = require('ws');
257  
258  const server = http.createServer();
259  const wss = new WebSocket.Server({ noServer: true });
260  
261  wss.on('connection', function connection(ws, request, client) {
262    ws.on('message', function message(msg) {
263      console.log(`Received message ${msg} from user ${client}`);
264    });
265  });
266  
267  server.on('upgrade', function upgrade(request, socket, head) {
268    // This function is not defined on purpose. Implement it with your own logic.
269    authenticate(request, (err, client) => {
270      if (err || !client) {
271        socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
272        socket.destroy();
273        return;
274      }
275  
276      wss.handleUpgrade(request, socket, head, function done(ws) {
277        wss.emit('connection', ws, request, client);
278      });
279    });
280  });
281  
282  server.listen(8080);
283  ```
284  
285  Also see the provided [example][session-parse-example] using `express-session`.
286  
287  ### Server broadcast
288  
289  A client WebSocket broadcasting to all connected WebSocket clients, including
290  itself.
291  
292  ```js
293  const WebSocket = require('ws');
294  
295  const wss = new WebSocket.Server({ port: 8080 });
296  
297  wss.on('connection', function connection(ws) {
298    ws.on('message', function incoming(data) {
299      wss.clients.forEach(function each(client) {
300        if (client.readyState === WebSocket.OPEN) {
301          client.send(data);
302        }
303      });
304    });
305  });
306  ```
307  
308  A client WebSocket broadcasting to every other connected WebSocket clients,
309  excluding itself.
310  
311  ```js
312  const WebSocket = require('ws');
313  
314  const wss = new WebSocket.Server({ port: 8080 });
315  
316  wss.on('connection', function connection(ws) {
317    ws.on('message', function incoming(data) {
318      wss.clients.forEach(function each(client) {
319        if (client !== ws && client.readyState === WebSocket.OPEN) {
320          client.send(data);
321        }
322      });
323    });
324  });
325  ```
326  
327  ### echo.websocket.org demo
328  
329  ```js
330  const WebSocket = require('ws');
331  
332  const ws = new WebSocket('wss://echo.websocket.org/', {
333    origin: 'https://websocket.org'
334  });
335  
336  ws.on('open', function open() {
337    console.log('connected');
338    ws.send(Date.now());
339  });
340  
341  ws.on('close', function close() {
342    console.log('disconnected');
343  });
344  
345  ws.on('message', function incoming(data) {
346    console.log(`Roundtrip time: ${Date.now() - data} ms`);
347  
348    setTimeout(function timeout() {
349      ws.send(Date.now());
350    }, 500);
351  });
352  ```
353  
354  ### Use the Node.js streams API
355  
356  ```js
357  const WebSocket = require('ws');
358  
359  const ws = new WebSocket('wss://echo.websocket.org/', {
360    origin: 'https://websocket.org'
361  });
362  
363  const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' });
364  
365  duplex.pipe(process.stdout);
366  process.stdin.pipe(duplex);
367  ```
368  
369  ### Other examples
370  
371  For a full example with a browser client communicating with a ws server, see the
372  examples folder.
373  
374  Otherwise, see the test cases.
375  
376  ## FAQ
377  
378  ### How to get the IP address of the client?
379  
380  The remote IP address can be obtained from the raw socket.
381  
382  ```js
383  const WebSocket = require('ws');
384  
385  const wss = new WebSocket.Server({ port: 8080 });
386  
387  wss.on('connection', function connection(ws, req) {
388    const ip = req.socket.remoteAddress;
389  });
390  ```
391  
392  When the server runs behind a proxy like NGINX, the de-facto standard is to use
393  the `X-Forwarded-For` header.
394  
395  ```js
396  wss.on('connection', function connection(ws, req) {
397    const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
398  });
399  ```
400  
401  ### How to detect and close broken connections?
402  
403  Sometimes the link between the server and the client can be interrupted in a way
404  that keeps both the server and the client unaware of the broken state of the
405  connection (e.g. when pulling the cord).
406  
407  In these cases ping messages can be used as a means to verify that the remote
408  endpoint is still responsive.
409  
410  ```js
411  const WebSocket = require('ws');
412  
413  function noop() {}
414  
415  function heartbeat() {
416    this.isAlive = true;
417  }
418  
419  const wss = new WebSocket.Server({ port: 8080 });
420  
421  wss.on('connection', function connection(ws) {
422    ws.isAlive = true;
423    ws.on('pong', heartbeat);
424  });
425  
426  const interval = setInterval(function ping() {
427    wss.clients.forEach(function each(ws) {
428      if (ws.isAlive === false) return ws.terminate();
429  
430      ws.isAlive = false;
431      ws.ping(noop);
432    });
433  }, 30000);
434  
435  wss.on('close', function close() {
436    clearInterval(interval);
437  });
438  ```
439  
440  Pong messages are automatically sent in response to ping messages as required by
441  the spec.
442  
443  Just like the server example above your clients might as well lose connection
444  without knowing it. You might want to add a ping listener on your clients to
445  prevent that. A simple implementation would be:
446  
447  ```js
448  const WebSocket = require('ws');
449  
450  function heartbeat() {
451    clearTimeout(this.pingTimeout);
452  
453    // Use `WebSocket#terminate()`, which immediately destroys the connection,
454    // instead of `WebSocket#close()`, which waits for the close timer.
455    // Delay should be equal to the interval at which your server
456    // sends out pings plus a conservative assumption of the latency.
457    this.pingTimeout = setTimeout(() => {
458      this.terminate();
459    }, 30000 + 1000);
460  }
461  
462  const client = new WebSocket('wss://echo.websocket.org/');
463  
464  client.on('open', heartbeat);
465  client.on('ping', heartbeat);
466  client.on('close', function clear() {
467    clearTimeout(this.pingTimeout);
468  });
469  ```
470  
471  ### How to connect via a proxy?
472  
473  Use a custom `http.Agent` implementation like [https-proxy-agent][] or
474  [socks-proxy-agent][].
475  
476  ## Changelog
477  
478  We're using the GitHub [releases][changelog] for changelog entries.
479  
480  ## License
481  
482  [MIT](LICENSE)
483  
484  [changelog]: https://github.com/websockets/ws/releases
485  [client-report]: http://websockets.github.io/ws/autobahn/clients/
486  [https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
487  [node-zlib-bug]: https://github.com/nodejs/node/issues/8871
488  [node-zlib-deflaterawdocs]:
489    https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
490  [permessage-deflate]: https://tools.ietf.org/html/rfc7692
491  [server-report]: http://websockets.github.io/ws/autobahn/servers/
492  [session-parse-example]: ./examples/express-session-parse
493  [socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
494  [ws-server-options]:
495    https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback