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