README.md
1 # ws: a Node.js WebSocket library 2 3 [](https://www.npmjs.com/package/ws) 4 [](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster) 5 [](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