UNPKG

@encharm/cws

Version:

cWS - fast C++ WebSocket implementation for Node.js

325 lines (252 loc) 9.77 kB
<h1 align="center">encharm/cWS</h1> <h6 align="center">Fast WebSocket implementation for Node.js and almost a drop-in replacement for the ws module</h6> ## Important Notes * **Consider using latest version of uWebSockets if you do not need compatibility with ws or a multi-process architecture.** * This repository is a fork of [ClusterWS/cWS](https://github.com/ClusterWS/cWS) * Prior, this repository was a fork of [uWebSockets v0.14](https://github.com/uNetworking/uWebSockets/tree/v0.14) therefore has two licence [MIT](https://github.com/encharm/cWS/blob/master/LICENSE) and [ZLIB](https://github.com/encharm/cWS/blob/master/src/LICENSE) ## Supported Node Versions (SSL) This table is true if you run ssl directly with `cws` (`Node.js`). In case if you use proxy for example `nginx`, `cws` can be run on bigger coverage. | cWS Version | Node 14 | Node 16 | Node 18 | Node 20 | |-------------|----------|---------|-------------------| | 4.4.x | X | X | X | X | Node <14 are supported with @clusterws/cws. ## Documentation #### Useful links * [Examples](./examples) * [Changelog](./CHANGELOG.md) #### Table of Contents * [Installation](#user-content-installation) * [Websocket Client](#user-content-websocket-client) * [Websocket Server](#user-content-websocket-server) * [Secure WebSocket](#user-content-secure-websocket) * [Handle App Level Ping In Browser (example)](#user-content-handle-app-level-ping-in-browser-example) ### Installation ```js npm i @encharm/cws ``` ### Websocket Client Typings: [dist/client.d.ts](https://github.com/encharm/cWS/blob/master/dist/client.d.ts) Import cws WebSocket: ```js const { WebSocket } = require('@encharm/cws'); ``` Connect to WebSocket server: ```js const socket = new WebSocket(/* ws server endpoint **/); ``` Event on `open` is triggered when server accepts connection and this connection is ready to perform other actions: ```js socket.on('open', () => { }); // or like browser interface socket.onopen = () => { }; ``` Event on `error` is triggered if there is an error with the connection: ```js socket.on('error', (err) => { }); // or like browser interface socket.onerror = (err) => { }; ``` Event on `close` is triggered when connection has been closed: ```js // code: number and reason: string are optional socket.on('close', (code, reason) => { }); // or like browser interface socket.onclose = (code, reason) => { }; ``` Event on `message` is triggered when client receives message(s): ```js // receives string or binary socket.on('message', (message) => { }); // or like browser interface socket.onmessage = (message) => { }; ``` Event `ping` is triggered when other side sends ping to check if connection still active: ```js socket.on('ping', () => { }); ``` Event `pong` is triggered after `ping` has been sent and it received back conformation: ```js socket.on('pong', () => { }) ``` To send message use `send` function: ```js // send accepts string or binary // will automatically identify type socket.send(msg); // will overwrite/specify message type to string (does not transform data to specified type) socket.send(msg, { binary: false }); // will overwrite/specify message type to binary (does not transform data to specified type) socket.send(msg, { binary: true }); // will call callback after message sent or errored socket.send(msg, null, (err) => { }); ``` To close connection you can use `close` or `terminate` methods: ```js // clean close code and reason are optional socket.close(code, reason); // destroy socket socket.terminate() ``` Use `ping` function to manually send ping: ```js socket.ping(); // now just wait for `pong` event ``` To get current socket ready state can use `readyState` getter: ```js socket.readyState; // -> OPEN (1) or CLOSED (3) // check if socket open can be done by if(socket.readyState === socket.OPEN) {} // check if socket closed can be done by if(socket.readyState === socket.CLOSED) {} ``` To find out number of buffered (and not yet sent) bytes: ```js socket.bufferedAmount; // useful for implementing back-pressure ``` To get addresses use `_socket` getter: ```js socket._socket; // Returns some thing like (all fields could be undefined): // { // remotePort, // remoteAddress, // remoteFamily // } ``` **For more information check typings (`*.d.ts`) files in [dist](https://github.com/encharm/cWS/blob/master/dist) folder** ### Websocket Server Typings: [dist/server.d.ts](https://github.com/encharm/cWS/blob/master/dist/server.d.ts) Import cws WebSocket: ```js const { WebSocket } = require('@encharm/cws'); ``` Create WebSocket server: ```js const wsServer = new WebSocket.Server({ /** * port?: number (creates server and listens on provided port) * host?: string (provide host if necessary with port) * path?: string (url at which accept ws connections) * server?: server (provide already existing server) * noDelay?: boolean (set socket no delay) * noServer?: boolean (use this when upgrade done outside of cws) * maxPayload?: number * perMessageDeflate?: boolean | { serverNoContextTakeover: boolean;} * verifyClient?: (info: ConnectionInfo, next: VerifyClientNext) => void (use to allow or decline connections) **/ }, () => { // callback called when server is ready // is not called when `noServer: true` or `server` is provided // from outside }); ``` Event on `connection` is triggered when new client is connected to the server: ```js // `ws` is websocket client all available options // can be found in `Websocket Client` section above // `req` is http upgrade request wsServer.on('connection', (ws, req) => {}) ``` Event on `error` is triggered when server has some issues and `noServer` is `false:` ```js // on error event will NOT include httpServer errors IF // server was passed under server parameter { server: httpServer }, // you can register on 'error' listener directly on passed server wsServer.on('error', (err) => { }) ``` Event on `close` is triggered after you call `wsServer.close()` function, if `cb` is provided both `cb` and on `close` listener will be triggered: ```js wsServer.on('close', () => { }) ``` To get all connected clients use `clients` getter: ```js wsServer.clients; // loop thought all clients: wsServer.clients.forEach((ws) => { }); // get number of clients: wsServer.clients.length; ``` To send message to all connected clients use `broadcast` method: ```js // broadcast string wsServer.broadcast(message); // broadcast binary wsServer.broadcast(message, { binary: true }); ``` cWS supports auto ping with `startAutoPing` function: ```js wsServer.startAutoPing(interval, appLevel); // send ping to each client every 10s and destroy client if no pong received wsServer.startAutoPing(10000); // pass true as second parameter to run app level ping // this is mainly used for browser to track pings at client level // as they do not expose on ping listener // look for browser client side implementation at the bottom // `Handle App Level Ping In Browser (example)` wsServer.startAutoPing(10000, true); ``` To stop server use `close` function: ```js wsServer.close(() => { // triggered after server has been stopped }) ``` `handleUpgrade` is function which is commonly used together with `noServer` (same as `ws` module) ```js const wss = new WebSocket.Server({ noServer: true }); const server = http.createServer(); wss.on('connection', (ws, req) => { }) server.on('upgrade', (request, socket, head) => { wss.handleUpgrade(request, socket, head, (ws) => { wss.emit('connection', ws, request); }); }); ``` **For more information check typings (`*.d.ts`) files in [dist](https://github.com/encharm/cWS/blob/master/dist) folder** ### Secure WebSocket You can use `wss://` with `cws` by providing `https` server to `cws` and setting `secureProtocol` on https options: ```js const { readFileSync } = require('fs'); const { createServer } = require('https'); const { WebSocket, secureProtocol } = require('@encharm/cws'); const options = { key: readFileSync(/** path to key */), cert: readFileSync(/** path to certificate */), secureProtocol // ...other Node HTTPS options }; const server = createServer(options); const wsServer = new WebSocket.Server({ server }); // your secure ws is ready (do your usual things) server.listen(port, () => { console.log('Server is running'); }) ``` **For more detail example check [examples](https://github.com/encharm/cWS/blob/master/examples) folder** ### Handle App Level Ping In Browser (example) Handling custom App level `ping`, `pong` from the client side which does not have `onping` and `onpong` listeners available such as browsers. **Note** if all your clients have `onping` and `onpong` listeners do not send `appLevelPing` ping from the server. If you enable appLevelPing you will need to implement similar handler for every client library which connects to the server. ```js const PING = 57; const PONG = new Uint8Array(['A'.charCodeAt()]); socket.binaryType = 'arraybuffer'; socket.onmessage = function (message) { // note actually sent message in // browser default WebSocket is under `message.data` // check if message is not string if (typeof message.data !== 'string') { // transform it to Uint8Array let buffer = new Uint8Array(message.data); // Check if it is actually ping from the server if (buffer.length === 1 && buffer[0] === PING) { // this is definitely `ping` event you can call custom on ping handler // also must send back immediately pong to the server // otherwise server will disconnect this client return socket.send(PONG); } } // process with your logic } ```