JavaScript 65.5%
Python 17.8%
CSS 13%
HTML 3.7%
1# ws: a Node.js WebSocket library23[](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)67ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and8server implementation.910Passes the quite extensive Autobahn test suite: [server][server-report],11[client][client-report].1213**Note**: This module does not work in the browser. The client in the docs is a14reference to a backend with the role of a client in the WebSocket communication.15Browser clients must use the native16[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)17object. To make the same code work seamlessly on Node.js and the browser, you18can use one of the many wrappers available on npm, like19[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).2021## Table of Contents2223- [Protocol support](#protocol-support)24- [Installing](#installing)25 - [Opt-in for performance](#opt-in-for-performance)26 - [Legacy opt-in for performance](#legacy-opt-in-for-performance)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 - [Round-trip time](#round-trip-time)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)4647## Protocol support4849- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)50- **HyBi drafts 13-17** (Current default, alternatively option51 `protocolVersion: 13`)5253## Installing5455```56npm install ws57```5859### Opt-in for performance6061[bufferutil][] is an optional module that can be installed alongside the ws62module:6364```65npm install --save-optional bufferutil66```6768This is a binary addon that improves the performance of certain operations such69as masking and unmasking the data payload of the WebSocket frames. Prebuilt70binaries are available for the most popular platforms, so you don't necessarily71need to have a C++ compiler installed on your machine.7273To force ws to not use bufferutil, use the74[`WS_NO_BUFFER_UTIL`](./doc/ws.md#ws_no_buffer_util) environment variable. This75can be useful to enhance security in systems where a user can put a package in76the package search path of an application of another user, due to how the77Node.js resolver algorithm works.7879#### Legacy opt-in for performance8081If you are running on an old version of Node.js (prior to v18.14.0), ws also82supports the [utf-8-validate][] module:8384```85npm install --save-optional utf-8-validate86```8788This contains a binary polyfill for [`buffer.isUtf8()`][].8990To force ws not to use utf-8-validate, use the91[`WS_NO_UTF_8_VALIDATE`](./doc/ws.md#ws_no_utf_8_validate) environment variable.9293## API docs9495See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and96utility functions.9798## WebSocket compression99100ws supports the [permessage-deflate extension][permessage-deflate] which enables101the client and server to negotiate a compression algorithm and its parameters,102and then selectively apply it to the data payloads of each WebSocket message.103104The extension is disabled by default on the server and enabled by default on the105client. It adds a significant overhead in terms of performance and memory106consumption so we suggest to enable it only if it is really needed.107108Note that Node.js has a variety of issues with high-performance compression,109where increased concurrency, especially on Linux, can lead to [catastrophic110memory fragmentation][node-zlib-bug] and slow performance. If you intend to use111permessage-deflate in production, it is worthwhile to set up a test112representative of your workload and ensure Node.js/zlib will handle it with113acceptable performance and memory usage.114115Tuning of permessage-deflate can be done via the options defined below. You can116also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly117into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].118119See [the docs][ws-server-options] for more options.120121```js122import WebSocket, { WebSocketServer } from 'ws';123124const wss = new WebSocketServer({125 port: 8080,126 perMessageDeflate: {127 zlibDeflateOptions: {128 // See zlib defaults.129 chunkSize: 1024,130 memLevel: 7,131 level: 3132 },133 zlibInflateOptions: {134 chunkSize: 10 * 1024135 },136 // Other options settable:137 clientNoContextTakeover: true, // Defaults to negotiated value.138 serverNoContextTakeover: true, // Defaults to negotiated value.139 serverMaxWindowBits: 10, // Defaults to negotiated value.140 // Below options specified as default values.141 concurrencyLimit: 10, // Limits zlib concurrency for perf.142 threshold: 1024 // Size (in bytes) below which messages143 // should not be compressed if context takeover is disabled.144 }145});146```147148The client will only use the extension if it is supported and enabled on the149server. To always disable the extension on the client, set the150`perMessageDeflate` option to `false`.151152```js153import WebSocket from 'ws';154155const ws = new WebSocket('ws://www.host.com/path', {156 perMessageDeflate: false157});158```159160## Usage examples161162### Sending and receiving text data163164```js165import WebSocket from 'ws';166167const ws = new WebSocket('ws://www.host.com/path');168169ws.on('error', console.error);170171ws.on('open', function open() {172 ws.send('something');173});174175ws.on('message', function message(data) {176 console.log('received: %s', data);177});178```179180### Sending binary data181182```js183import WebSocket from 'ws';184185const ws = new WebSocket('ws://www.host.com/path');186187ws.on('error', console.error);188189ws.on('open', function open() {190 const array = new Float32Array(5);191192 for (var i = 0; i < array.length; ++i) {193 array[i] = i / 2;194 }195196 ws.send(array);197});198```199200### Simple server201202```js203import { WebSocketServer } from 'ws';204205const wss = new WebSocketServer({ port: 8080 });206207wss.on('connection', function connection(ws) {208 ws.on('error', console.error);209210 ws.on('message', function message(data) {211 console.log('received: %s', data);212 });213214 ws.send('something');215});216```217218### External HTTP/S server219220```js221import { createServer } from 'https';222import { readFileSync } from 'fs';223import { WebSocketServer } from 'ws';224225const server = createServer({226 cert: readFileSync('/path/to/cert.pem'),227 key: readFileSync('/path/to/key.pem')228});229const wss = new WebSocketServer({ server });230231wss.on('connection', function connection(ws) {232 ws.on('error', console.error);233234 ws.on('message', function message(data) {235 console.log('received: %s', data);236 });237238 ws.send('something');239});240241server.listen(8080);242```243244### Multiple servers sharing a single HTTP/S server245246```js247import { createServer } from 'http';248import { WebSocketServer } from 'ws';249250const server = createServer();251const wss1 = new WebSocketServer({ noServer: true });252const wss2 = new WebSocketServer({ noServer: true });253254wss1.on('connection', function connection(ws) {255 ws.on('error', console.error);256257 // ...258});259260wss2.on('connection', function connection(ws) {261 ws.on('error', console.error);262263 // ...264});265266server.on('upgrade', function upgrade(request, socket, head) {267 const { pathname } = new URL(request.url, 'wss://base.url');268269 if (pathname === '/foo') {270 wss1.handleUpgrade(request, socket, head, function done(ws) {271 wss1.emit('connection', ws, request);272 });273 } else if (pathname === '/bar') {274 wss2.handleUpgrade(request, socket, head, function done(ws) {275 wss2.emit('connection', ws, request);276 });277 } else {278 socket.destroy();279 }280});281282server.listen(8080);283```284285### Client authentication286287```js288import { createServer } from 'http';289import { WebSocketServer } from 'ws';290291function onSocketError(err) {292 console.error(err);293}294295const server = createServer();296const wss = new WebSocketServer({ noServer: true });297298wss.on('connection', function connection(ws, request, client) {299 ws.on('error', console.error);300301 ws.on('message', function message(data) {302 console.log(`Received message ${data} from user ${client}`);303 });304});305306server.on('upgrade', function upgrade(request, socket, head) {307 socket.on('error', onSocketError);308309 // This function is not defined on purpose. Implement it with your own logic.310 authenticate(request, function next(err, client) {311 if (err || !client) {312 socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');313 socket.destroy();314 return;315 }316317 socket.removeListener('error', onSocketError);318319 wss.handleUpgrade(request, socket, head, function done(ws) {320 wss.emit('connection', ws, request, client);321 });322 });323});324325server.listen(8080);326```327328Also see the provided [example][session-parse-example] using `express-session`.329330### Server broadcast331332A client WebSocket broadcasting to all connected WebSocket clients, including333itself.334335```js336import WebSocket, { WebSocketServer } from 'ws';337338const wss = new WebSocketServer({ port: 8080 });339340wss.on('connection', function connection(ws) {341 ws.on('error', console.error);342343 ws.on('message', function message(data, isBinary) {344 wss.clients.forEach(function each(client) {345 if (client.readyState === WebSocket.OPEN) {346 client.send(data, { binary: isBinary });347 }348 });349 });350});351```352353A client WebSocket broadcasting to every other connected WebSocket clients,354excluding itself.355356```js357import WebSocket, { WebSocketServer } from 'ws';358359const wss = new WebSocketServer({ port: 8080 });360361wss.on('connection', function connection(ws) {362 ws.on('error', console.error);363364 ws.on('message', function message(data, isBinary) {365 wss.clients.forEach(function each(client) {366 if (client !== ws && client.readyState === WebSocket.OPEN) {367 client.send(data, { binary: isBinary });368 }369 });370 });371});372```373374### Round-trip time375376```js377import WebSocket from 'ws';378379const ws = new WebSocket('wss://websocket-echo.com/');380381ws.on('error', console.error);382383ws.on('open', function open() {384 console.log('connected');385 ws.send(Date.now());386});387388ws.on('close', function close() {389 console.log('disconnected');390});391392ws.on('message', function message(data) {393 console.log(`Round-trip time: ${Date.now() - data} ms`);394395 setTimeout(function timeout() {396 ws.send(Date.now());397 }, 500);398});399```400401### Use the Node.js streams API402403```js404import WebSocket, { createWebSocketStream } from 'ws';405406const ws = new WebSocket('wss://websocket-echo.com/');407408const duplex = createWebSocketStream(ws, { encoding: 'utf8' });409410duplex.on('error', console.error);411412duplex.pipe(process.stdout);413process.stdin.pipe(duplex);414```415416### Other examples417418For a full example with a browser client communicating with a ws server, see the419examples folder.420421Otherwise, see the test cases.422423## FAQ424425### How to get the IP address of the client?426427The remote IP address can be obtained from the raw socket.428429```js430import { WebSocketServer } from 'ws';431432const wss = new WebSocketServer({ port: 8080 });433434wss.on('connection', function connection(ws, req) {435 const ip = req.socket.remoteAddress;436437 ws.on('error', console.error);438});439```440441When the server runs behind a proxy like NGINX, the de-facto standard is to use442the `X-Forwarded-For` header.443444```js445wss.on('connection', function connection(ws, req) {446 const ip = req.headers['x-forwarded-for'].split(',')[0].trim();447448 ws.on('error', console.error);449});450```451452### How to detect and close broken connections?453454Sometimes, the link between the server and the client can be interrupted in a455way that keeps both the server and the client unaware of the broken state of the456connection (e.g. when pulling the cord).457458In these cases, ping messages can be used as a means to verify that the remote459endpoint is still responsive.460461```js462import { WebSocketServer } from 'ws';463464function heartbeat() {465 this.isAlive = true;466}467468const wss = new WebSocketServer({ port: 8080 });469470wss.on('connection', function connection(ws) {471 ws.isAlive = true;472 ws.on('error', console.error);473 ws.on('pong', heartbeat);474});475476const interval = setInterval(function ping() {477 wss.clients.forEach(function each(ws) {478 if (ws.isAlive === false) return ws.terminate();479480 ws.isAlive = false;481 ws.ping();482 });483}, 30000);484485wss.on('close', function close() {486 clearInterval(interval);487});488```489490Pong messages are automatically sent in response to ping messages as required by491the spec.492493Just like the server example above, your clients might as well lose connection494without knowing it. You might want to add a ping listener on your clients to495prevent that. A simple implementation would be:496497```js498import WebSocket from 'ws';499500function heartbeat() {501 clearTimeout(this.pingTimeout);502503 // Use `WebSocket#terminate()`, which immediately destroys the connection,504 // instead of `WebSocket#close()`, which waits for the close timer.505 // Delay should be equal to the interval at which your server506 // sends out pings plus a conservative assumption of the latency.507 this.pingTimeout = setTimeout(() => {508 this.terminate();509 }, 30000 + 1000);510}511512const client = new WebSocket('wss://websocket-echo.com/');513514client.on('error', console.error);515client.on('open', heartbeat);516client.on('ping', heartbeat);517client.on('close', function clear() {518 clearTimeout(this.pingTimeout);519});520```521522### How to connect via a proxy?523524Use a custom `http.Agent` implementation like [https-proxy-agent][] or525[socks-proxy-agent][].526527## Changelog528529We're using the GitHub [releases][changelog] for changelog entries.530531## License532533[MIT](LICENSE)534535[`buffer.isutf8()`]: https://nodejs.org/api/buffer.html#bufferisutf8input536[bufferutil]: https://github.com/websockets/bufferutil537[changelog]: https://github.com/websockets/ws/releases538[client-report]: http://websockets.github.io/ws/autobahn/clients/539[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent540[node-zlib-bug]: https://github.com/nodejs/node/issues/8871541[node-zlib-deflaterawdocs]:542 https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options543[permessage-deflate]: https://tools.ietf.org/html/rfc7692544[server-report]: http://websockets.github.io/ws/autobahn/servers/545[session-parse-example]: ./examples/express-session-parse546[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent547[utf-8-validate]: https://github.com/websockets/utf-8-validate548[ws-server-options]: ./doc/ws.md#new-websocketserveroptions-callback549