README.md (12904B)
1 # ws: a Node.js WebSocket library 2 3 [![Version npm](https://img.shields.io/npm/v/ws.svg)](https://www.npmjs.com/package/ws) 4 [![Linux Build](https://img.shields.io/travis/websockets/ws/master.svg)](https://travis-ci.org/websockets/ws) 5 [![Windows Build](https://ci.appveyor.com/api/projects/status/github/websockets/ws?branch=master&svg=true)](https://ci.appveyor.com/project/lpinca/ws) 6 [![Coverage Status](https://img.shields.io/coveralls/websockets/ws/master.svg)](https://coveralls.io/r/websockets/ws?branch=master) 7 8 ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and 9 server implementation. 10 11 Passes the quite extensive Autobahn test suite: [server][server-report], 12 [client][client-report]. 13 14 **Note**: This module does not work in the browser. The client in the docs is a 15 reference to a back end with the role of a client in the WebSocket 16 communication. Browser clients must use the native 17 [`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) 18 object. To make the same code work seamlessly on Node.js and the browser, you 19 can use one of the many wrappers available on npm, like 20 [isomorphic-ws](https://github.com/heineiuo/isomorphic-ws). 21 22 ## Table of Contents 23 24 - [Protocol support](#protocol-support) 25 - [Installing](#installing) 26 - [Opt-in for performance and spec compliance](#opt-in-for-performance-and-spec-compliance) 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 - [Server broadcast](#server-broadcast) 36 - [echo.websocket.org demo](#echowebsocketorg-demo) 37 - [Other examples](#other-examples) 38 - [Error handling best practices](#error-handling-best-practices) 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 and spec compliance 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 as required by the spec. 70 71 ## API docs 72 73 See [`/doc/ws.md`](./doc/ws.md) for Node.js-like docs for the ws classes. 74 75 ## WebSocket compression 76 77 ws supports the [permessage-deflate extension][permessage-deflate] which enables 78 the client and server to negotiate a compression algorithm and its parameters, 79 and then selectively apply it to the data payloads of each WebSocket message. 80 81 The extension is disabled by default on the server and enabled by default on the 82 client. It adds a significant overhead in terms of performance and memory 83 consumption so we suggest to enable it only if it is really needed. 84 85 Note that Node.js has a variety of issues with high-performance compression, 86 where increased concurrency, especially on Linux, can lead to [catastrophic 87 memory fragmentation][node-zlib-bug] and slow performance. If you intend to use 88 permessage-deflate in production, it is worthwhile to set up a test 89 representative of your workload and ensure Node.js/zlib will handle it with 90 acceptable performance and memory usage. 91 92 Tuning of permessage-deflate can be done via the options defined below. You can 93 also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly 94 into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs]. 95 96 See [the docs][ws-server-options] for more options. 97 98 ```js 99 const WebSocket = require('ws'); 100 101 const wss = new WebSocket.Server({ 102 port: 8080, 103 perMessageDeflate: { 104 zlibDeflateOptions: { 105 // See zlib defaults. 106 chunkSize: 1024, 107 memLevel: 7, 108 level: 3 109 }, 110 zlibInflateOptions: { 111 chunkSize: 10 * 1024 112 }, 113 // Other options settable: 114 clientNoContextTakeover: true, // Defaults to negotiated value. 115 serverNoContextTakeover: true, // Defaults to negotiated value. 116 serverMaxWindowBits: 10, // Defaults to negotiated value. 117 // Below options specified as default values. 118 concurrencyLimit: 10, // Limits zlib concurrency for perf. 119 threshold: 1024 // Size (in bytes) below which messages 120 // should not be compressed. 121 } 122 }); 123 ``` 124 125 The client will only use the extension if it is supported and enabled on the 126 server. To always disable the extension on the client set the 127 `perMessageDeflate` option to `false`. 128 129 ```js 130 const WebSocket = require('ws'); 131 132 const ws = new WebSocket('ws://www.host.com/path', { 133 perMessageDeflate: false 134 }); 135 ``` 136 137 ## Usage examples 138 139 ### Sending and receiving text data 140 141 ```js 142 const WebSocket = require('ws'); 143 144 const ws = new WebSocket('ws://www.host.com/path'); 145 146 ws.on('open', function open() { 147 ws.send('something'); 148 }); 149 150 ws.on('message', function incoming(data) { 151 console.log(data); 152 }); 153 ``` 154 155 ### Sending binary data 156 157 ```js 158 const WebSocket = require('ws'); 159 160 const ws = new WebSocket('ws://www.host.com/path'); 161 162 ws.on('open', function open() { 163 const array = new Float32Array(5); 164 165 for (var i = 0; i < array.length; ++i) { 166 array[i] = i / 2; 167 } 168 169 ws.send(array); 170 }); 171 ``` 172 173 ### Simple server 174 175 ```js 176 const WebSocket = require('ws'); 177 178 const wss = new WebSocket.Server({ port: 8080 }); 179 180 wss.on('connection', function connection(ws) { 181 ws.on('message', function incoming(message) { 182 console.log('received: %s', message); 183 }); 184 185 ws.send('something'); 186 }); 187 ``` 188 189 ### External HTTP/S server 190 191 ```js 192 const fs = require('fs'); 193 const https = require('https'); 194 const WebSocket = require('ws'); 195 196 const server = new https.createServer({ 197 cert: fs.readFileSync('/path/to/cert.pem'), 198 key: fs.readFileSync('/path/to/key.pem') 199 }); 200 const wss = new WebSocket.Server({ server }); 201 202 wss.on('connection', function connection(ws) { 203 ws.on('message', function incoming(message) { 204 console.log('received: %s', message); 205 }); 206 207 ws.send('something'); 208 }); 209 210 server.listen(8080); 211 ``` 212 213 ### Multiple servers sharing a single HTTP/S server 214 215 ```js 216 const http = require('http'); 217 const WebSocket = require('ws'); 218 219 const server = http.createServer(); 220 const wss1 = new WebSocket.Server({ noServer: true }); 221 const wss2 = new WebSocket.Server({ noServer: true }); 222 223 wss1.on('connection', function connection(ws) { 224 // ... 225 }); 226 227 wss2.on('connection', function connection(ws) { 228 // ... 229 }); 230 231 server.on('upgrade', function upgrade(request, socket, head) { 232 const pathname = url.parse(request.url).pathname; 233 234 if (pathname === '/foo') { 235 wss1.handleUpgrade(request, socket, head, function done(ws) { 236 wss1.emit('connection', ws, request); 237 }); 238 } else if (pathname === '/bar') { 239 wss2.handleUpgrade(request, socket, head, function done(ws) { 240 wss2.emit('connection', ws, request); 241 }); 242 } else { 243 socket.destroy(); 244 } 245 }); 246 247 server.listen(8080); 248 ``` 249 250 ### Server broadcast 251 252 ```js 253 const WebSocket = require('ws'); 254 255 const wss = new WebSocket.Server({ port: 8080 }); 256 257 // Broadcast to all. 258 wss.broadcast = function broadcast(data) { 259 wss.clients.forEach(function each(client) { 260 if (client.readyState === WebSocket.OPEN) { 261 client.send(data); 262 } 263 }); 264 }; 265 266 wss.on('connection', function connection(ws) { 267 ws.on('message', function incoming(data) { 268 // Broadcast to everyone else. 269 wss.clients.forEach(function each(client) { 270 if (client !== ws && client.readyState === WebSocket.OPEN) { 271 client.send(data); 272 } 273 }); 274 }); 275 }); 276 ``` 277 278 ### echo.websocket.org demo 279 280 ```js 281 const WebSocket = require('ws'); 282 283 const ws = new WebSocket('wss://echo.websocket.org/', { 284 origin: 'https://websocket.org' 285 }); 286 287 ws.on('open', function open() { 288 console.log('connected'); 289 ws.send(Date.now()); 290 }); 291 292 ws.on('close', function close() { 293 console.log('disconnected'); 294 }); 295 296 ws.on('message', function incoming(data) { 297 console.log(`Roundtrip time: ${Date.now() - data} ms`); 298 299 setTimeout(function timeout() { 300 ws.send(Date.now()); 301 }, 500); 302 }); 303 ``` 304 305 ### Other examples 306 307 For a full example with a browser client communicating with a ws server, see the 308 examples folder. 309 310 Otherwise, see the test cases. 311 312 ## Error handling best practices 313 314 ```js 315 // If the WebSocket is closed before the following send is attempted 316 ws.send('something'); 317 318 // Errors (both immediate and async write errors) can be detected in an optional 319 // callback. The callback is also the only way of being notified that data has 320 // actually been sent. 321 ws.send('something', function ack(error) { 322 // If error is not defined, the send has been completed, otherwise the error 323 // object will indicate what failed. 324 }); 325 326 // Immediate errors can also be handled with `try...catch`, but **note** that 327 // since sends are inherently asynchronous, socket write failures will *not* be 328 // captured when this technique is used. 329 try { 330 ws.send('something'); 331 } catch (e) { 332 /* handle error */ 333 } 334 ``` 335 336 ## FAQ 337 338 ### How to get the IP address of the client? 339 340 The remote IP address can be obtained from the raw socket. 341 342 ```js 343 const WebSocket = require('ws'); 344 345 const wss = new WebSocket.Server({ port: 8080 }); 346 347 wss.on('connection', function connection(ws, req) { 348 const ip = req.connection.remoteAddress; 349 }); 350 ``` 351 352 When the server runs behind a proxy like NGINX, the de-facto standard is to use 353 the `X-Forwarded-For` header. 354 355 ```js 356 wss.on('connection', function connection(ws, req) { 357 const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0]; 358 }); 359 ``` 360 361 ### How to detect and close broken connections? 362 363 Sometimes the link between the server and the client can be interrupted in a way 364 that keeps both the server and the client unaware of the broken state of the 365 connection (e.g. when pulling the cord). 366 367 In these cases ping messages can be used as a means to verify that the remote 368 endpoint is still responsive. 369 370 ```js 371 const WebSocket = require('ws'); 372 373 function noop() {} 374 375 function heartbeat() { 376 this.isAlive = true; 377 } 378 379 const wss = new WebSocket.Server({ port: 8080 }); 380 381 wss.on('connection', function connection(ws) { 382 ws.isAlive = true; 383 ws.on('pong', heartbeat); 384 }); 385 386 const interval = setInterval(function ping() { 387 wss.clients.forEach(function each(ws) { 388 if (ws.isAlive === false) return ws.terminate(); 389 390 ws.isAlive = false; 391 ws.ping(noop); 392 }); 393 }, 30000); 394 ``` 395 396 Pong messages are automatically sent in response to ping messages as required by 397 the spec. 398 399 Just like the server example above your clients might as well lose connection 400 without knowing it. You might want to add a ping listener on your clients to 401 prevent that. A simple implementation would be: 402 403 ```js 404 const WebSocket = require('ws'); 405 406 function heartbeat() { 407 clearTimeout(this.pingTimeout); 408 409 // Use `WebSocket#terminate()` and not `WebSocket#close()`. Delay should be 410 // equal to the interval at which your server sends out pings plus a 411 // conservative assumption of the latency. 412 this.pingTimeout = setTimeout(() => { 413 this.terminate(); 414 }, 30000 + 1000); 415 } 416 417 const client = new WebSocket('wss://echo.websocket.org/'); 418 419 client.on('open', heartbeat); 420 client.on('ping', heartbeat); 421 client.on('close', function clear() { 422 clearTimeout(this.pingTimeout); 423 }); 424 ``` 425 426 ### How to connect via a proxy? 427 428 Use a custom `http.Agent` implementation like [https-proxy-agent][] or 429 [socks-proxy-agent][]. 430 431 ## Changelog 432 433 We're using the GitHub [releases][changelog] for changelog entries. 434 435 ## License 436 437 [MIT](LICENSE) 438 439 [https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent 440 [socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent 441 [client-report]: http://websockets.github.io/ws/autobahn/clients/ 442 [server-report]: http://websockets.github.io/ws/autobahn/servers/ 443 [permessage-deflate]: https://tools.ietf.org/html/rfc7692 444 [changelog]: https://github.com/websockets/ws/releases 445 [node-zlib-bug]: https://github.com/nodejs/node/issues/8871 446 [node-zlib-deflaterawdocs]: 447 https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options 448 [ws-server-options]: 449 https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback