README.md (13967B)
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 [![Build](https://img.shields.io/travis/websockets/ws/master.svg?logo=travis)](https://travis-ci.com/websockets/ws) 5 [![Windows x86 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 // This function is not defined on purpose. Implement it with your own logic. 270 authenticate(request, (err, client) => { 271 if (err || !client) { 272 socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); 273 socket.destroy(); 274 return; 275 } 276 277 wss.handleUpgrade(request, socket, head, function done(ws) { 278 wss.emit('connection', ws, request, client); 279 }); 280 }); 281 }); 282 283 server.listen(8080); 284 ``` 285 286 Also see the provided [example][session-parse-example] using `express-session`. 287 288 ### Server broadcast 289 290 A client WebSocket broadcasting to all connected WebSocket clients, including 291 itself. 292 293 ```js 294 const WebSocket = require('ws'); 295 296 const wss = new WebSocket.Server({ port: 8080 }); 297 298 wss.on('connection', function connection(ws) { 299 ws.on('message', function incoming(data) { 300 wss.clients.forEach(function each(client) { 301 if (client.readyState === WebSocket.OPEN) { 302 client.send(data); 303 } 304 }); 305 }); 306 }); 307 ``` 308 309 A client WebSocket broadcasting to every other connected WebSocket clients, 310 excluding itself. 311 312 ```js 313 const WebSocket = require('ws'); 314 315 const wss = new WebSocket.Server({ port: 8080 }); 316 317 wss.on('connection', function connection(ws) { 318 ws.on('message', function incoming(data) { 319 wss.clients.forEach(function each(client) { 320 if (client !== ws && client.readyState === WebSocket.OPEN) { 321 client.send(data); 322 } 323 }); 324 }); 325 }); 326 ``` 327 328 ### echo.websocket.org demo 329 330 ```js 331 const WebSocket = require('ws'); 332 333 const ws = new WebSocket('wss://echo.websocket.org/', { 334 origin: 'https://websocket.org' 335 }); 336 337 ws.on('open', function open() { 338 console.log('connected'); 339 ws.send(Date.now()); 340 }); 341 342 ws.on('close', function close() { 343 console.log('disconnected'); 344 }); 345 346 ws.on('message', function incoming(data) { 347 console.log(`Roundtrip time: ${Date.now() - data} ms`); 348 349 setTimeout(function timeout() { 350 ws.send(Date.now()); 351 }, 500); 352 }); 353 ``` 354 355 ### Use the Node.js streams API 356 357 ```js 358 const WebSocket = require('ws'); 359 360 const ws = new WebSocket('wss://echo.websocket.org/', { 361 origin: 'https://websocket.org' 362 }); 363 364 const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' }); 365 366 duplex.pipe(process.stdout); 367 process.stdin.pipe(duplex); 368 ``` 369 370 ### Other examples 371 372 For a full example with a browser client communicating with a ws server, see the 373 examples folder. 374 375 Otherwise, see the test cases. 376 377 ## FAQ 378 379 ### How to get the IP address of the client? 380 381 The remote IP address can be obtained from the raw socket. 382 383 ```js 384 const WebSocket = require('ws'); 385 386 const wss = new WebSocket.Server({ port: 8080 }); 387 388 wss.on('connection', function connection(ws, req) { 389 const ip = req.socket.remoteAddress; 390 }); 391 ``` 392 393 When the server runs behind a proxy like NGINX, the de-facto standard is to use 394 the `X-Forwarded-For` header. 395 396 ```js 397 wss.on('connection', function connection(ws, req) { 398 const ip = req.headers['x-forwarded-for'].split(/\s*,\s*/)[0]; 399 }); 400 ``` 401 402 ### How to detect and close broken connections? 403 404 Sometimes the link between the server and the client can be interrupted in a way 405 that keeps both the server and the client unaware of the broken state of the 406 connection (e.g. when pulling the cord). 407 408 In these cases ping messages can be used as a means to verify that the remote 409 endpoint is still responsive. 410 411 ```js 412 const WebSocket = require('ws'); 413 414 function noop() {} 415 416 function heartbeat() { 417 this.isAlive = true; 418 } 419 420 const wss = new WebSocket.Server({ port: 8080 }); 421 422 wss.on('connection', function connection(ws) { 423 ws.isAlive = true; 424 ws.on('pong', heartbeat); 425 }); 426 427 const interval = setInterval(function ping() { 428 wss.clients.forEach(function each(ws) { 429 if (ws.isAlive === false) return ws.terminate(); 430 431 ws.isAlive = false; 432 ws.ping(noop); 433 }); 434 }, 30000); 435 436 wss.on('close', function close() { 437 clearInterval(interval); 438 }); 439 ``` 440 441 Pong messages are automatically sent in response to ping messages as required by 442 the spec. 443 444 Just like the server example above your clients might as well lose connection 445 without knowing it. You might want to add a ping listener on your clients to 446 prevent that. A simple implementation would be: 447 448 ```js 449 const WebSocket = require('ws'); 450 451 function heartbeat() { 452 clearTimeout(this.pingTimeout); 453 454 // Use `WebSocket#terminate()`, which immediately destroys the connection, 455 // instead of `WebSocket#close()`, which waits for the close timer. 456 // Delay should be equal to the interval at which your server 457 // sends out pings plus a conservative assumption of the latency. 458 this.pingTimeout = setTimeout(() => { 459 this.terminate(); 460 }, 30000 + 1000); 461 } 462 463 const client = new WebSocket('wss://echo.websocket.org/'); 464 465 client.on('open', heartbeat); 466 client.on('ping', heartbeat); 467 client.on('close', function clear() { 468 clearTimeout(this.pingTimeout); 469 }); 470 ``` 471 472 ### How to connect via a proxy? 473 474 Use a custom `http.Agent` implementation like [https-proxy-agent][] or 475 [socks-proxy-agent][]. 476 477 ## Changelog 478 479 We're using the GitHub [releases][changelog] for changelog entries. 480 481 ## License 482 483 [MIT](LICENSE) 484 485 [changelog]: https://github.com/websockets/ws/releases 486 [client-report]: http://websockets.github.io/ws/autobahn/clients/ 487 [https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent 488 [node-zlib-bug]: https://github.com/nodejs/node/issues/8871 489 [node-zlib-deflaterawdocs]: 490 https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options 491 [permessage-deflate]: https://tools.ietf.org/html/rfc7692 492 [server-report]: http://websockets.github.io/ws/autobahn/servers/ 493 [session-parse-example]: ./examples/express-session-parse 494 [socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent 495 [ws-server-options]: 496 https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback