An open API service indexing awesome lists of open source software.

https://github.com/dimdengd/ultimate-ws

The Ultimate WebSocket server. Fastest ws-compatible server, based on ยตWebSockets.
https://github.com/dimdengd/ultimate-ws

Last synced: about 1 year ago
JSON representation

The Ultimate WebSocket server. Fastest ws-compatible server, based on ยตWebSockets.

Awesome Lists containing this project

README

          

# Ultimate WS

The *Ultimate* WebSocket server. Ultimate WS is an extremely fast, drop-in replacement for `ws` module, with support for [Ultimate Express](https://github.com/dimdenGD/ultimate-express) http upgrades. It uses [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js) under the hood.

It's useful when:
- You want same API as `ws` module, but the speed of `uWebSockets.js`
- You want to convert your Express app with `ws` to use [Ultimate Express](https://github.com/dimdenGD/ultimate-express) instead

[![Node.js >= 16.0.0](https://img.shields.io/badge/Node.js-%3E=16.0.0-green)](https://nodejs.org)
[![npm](https://img.shields.io/npm/v/ultimate-ws?label=last+version)](https://npmjs.com/package/ultimate-ws)
[![Patreon](https://img.shields.io/badge/donate-Patreon-orange)](https://patreon.com/dimdendev)

## Installation

1.
```bash
npm install ultimate-ws
```
2. Replace `ws` with `ultimate-ws` in your code
3. Check compatibility and differences below

## Performance

Echo test using `artillery` (duration: 20, arrivalRate: 10000):

| Module | Send rate | Mean Session length | Median Session length |
|-------------------|---------------|---------------------|-----------------------|
| `ws` | 2709/sec | 2535ms | 127ms |
| **`ultimate-ws`** | **10046/sec** | **45ms** | **12ms** |

## Usage

### Use with [Ultimate Express](https://github.com/dimdenGD/ultimate-express)

Since you don't create `http` server for `ws` or `express`, you can't really use `server.on("upgrade", ...)` to upgrade to Ultimate WS. Instead, you can pass Ultimate Express or uWS app to `WebSocketServer` as option. So **instead** of doing this:
```js
const http = require("http");
const express = require("express");
const ws = require("ws");

const app = express();
const wsServer = new ws.WebSocketServer({ noServer: true });

app.get("/", (_, res) => res.send("Hello, world!"));

const server = http.createServer(app);
server.on("upgrade", (request, socket, head) => {
const { pathname } = url.parse(request.url);
if(pathname !== "/wspath") return request.socket.destroy();

wsServer.handleUpgrade(request, socket, head, (ws) => {
wsServer.emit("connection", ws, request);
});
});

server.listen(3000);
```
You need to do this:
```js
const express = require("ultimate-express");
const app = express();
const { WebSocketServer } = require("ultimate-ws");

app.get("/", (_, res) => res.send("Hello, world!"));

const wsServer = new WebSocketServer({ server: app, path: "/wspath" }); // path is optional

// your usual `ws` server code ...

app.listen(3000);
```

[Ultimate Express](https://github.com/dimdenGD/ultimate-express) is fully compatible, much faster `express` implementation.

### Stand-alone usage

If you want to use Ultimate WS without any existing http server, you can do this:
```js
const { WebSocketServer } = require("ultimate-ws");

const wsServer = new WebSocketServer({ port: 3000 });

// your usual `ws` server code ...
wsServer.on("connection", (ws) => {
ws.on("message", (message) => {
ws.send(message);
});
});
```

### Use with existing uWS server

You can also pass existing uWS server to `WebSocketServer`:
```js
const { WebSocketServer } = require("ultimate-ws");
const uws = require("uWebSockets.js");

const server = uws.App();
const wsServer = new WebSocketServer({ server: server });

// your usual `ws` server code ...

server.listen(3000);
```

## HTTPS

Ultimate WS supports HTTPS. You can pass `uwsOptions` to `WebSocketServer` to configure it:
```js
const { WebSocketServer } = require("ultimate-ws");

const wsServer = new WebSocketServer({
port: 3000,
uwsOptions: {
// https://unetworking.github.io/uWebSockets.js/generated/interfaces/AppOptions.html
key_file_name: "path/to/key.pem",
cert_file_name: "path/to/cert.pem",
}
});
```

## Handling requests before connection

Instead of this:

```js
const http = require("http");
const express = require("express");
const ws = require("ws");

const app = express();
const wsServer = new ws.WebSocketServer({ noServer: true, path: "/wspath" });

app.get("/", (_, res) => res.send("Hello, world!"));

const server = http.createServer(app);
server.on("upgrade", async (request, socket, head) => {
// your auth logic
const user = await getUserFromDatabase(request.headers['authorization']);
if(!user) return socket.destroy();

wsServer.handleUpgrade(request, socket, head, (ws) => {
ws.user = user;
wsServer.emit("connection", ws, request);
});
});

server.listen(3000);
```

You should do this:
```js
const { WebSocketServer } = require("ultimate-ws");
const express = require("ultimate-express");

const app = express();

const wsServer = new WebSocketServer({
server: app,
path: "/wspath",
handleUpgrade: async (request) => {
// your auth logic
const user = await getUserFromDatabase(request.headers['authorization']);
if(!user) {
// request has `req` and `res` properties
// which are instances of `uws.HttpRequest` and `uws.HttpResponse`
request.res.cork(() => {
request.res.writeStatus("401 Unauthorized");
request.res.end();
});
return false;
}

return (ws, request) => {
ws.user = user;
wsServer.emit("connection", ws, request);
}
}
});

app.get("/", (_, res) => res.send("Hello, world!"));

app.listen(3000);
```

- if `handleUpgrade` returns a function, it will be called with the new `WebSocket` instance and original request. "connection" will not be emitted automatically.
- if it returns `false`, the connection will not be upgraded. It's your responsibility to destroy the socket.
- if it returns nothing or anything else, the connection will be handled as usual, and "connection" event will be emitted.
- By default (`handleUpgrade: undefined`), the connection will be handled as usual, and "connection" event will be emitted.

## Compatibility

All commonly used `ws` features are supported. Almost all ws servers should work, as it's built with maximum compatibility in mind.
Please refer to [ws module documentation](https://github.com/websockets/ws) for API reference.
Below is the list of supported features and their compatibility:

โœ… - Full support (all features and options are supported)
๐Ÿšง - Partial support (some features are not supported)
โŒ - Not supported

#### WebSocket

- โœ… WebSocket
- โœ… WebSocket.Server
- โœ… WebSocket.WebSocket
- โœ… WebSocket.WebSocketServer
- โœ… WebSocket.CONNECTING
- โœ… WebSocket.OPEN
- โœ… WebSocket.CLOSING
- โœ… WebSocket.CLOSED
- โœ… WebSocket.createWebSocketStream

### Server

#### Server options

`ws` options:

- โœ… autoPong (maps to uWS `sendPingsAutomatically`, default `true`)
- โœ… allowSynchronousEvents (may lower performance when `false`, default `true`)
- โœ… clientTracking
- โœ… handleProtocols
- โœ… host
- โœ… maxPayload (default 100mb)
- โœ… path
- ๐Ÿšง perMessageDeflate - pass `true` for `DEDICATED_COMPRESSOR_4KB | DEDICATED_DECOMPRESSOR` or your own [`CompressOptions`](https://unetworking.github.io/uWebSockets.js/generated/types/CompressOptions.html) number. Options are not supported and if this is an object it will be treated as `true`.
- โœ… port
- โœ… server
- โœ… verifyClient
- โœ… WebSocket
- โœ… callback
- โŒ noServer - see examples above for alternatives
- โŒ skipUTF8Validation - uWS always validates UTF-8 when message is a string
- โŒ backlog

`uWS` options (additional options that `ws` doesn't have):

- โœ… maxBackpressure (default `maxPayload`)
- โœ… idleTimeout (default 120)
- โœ… maxLifetime (default 0)
- โœ… closeOnBackpressureLimit (default `false`)

#### Server events

- โœ… close
- โœ… connection
- โœ… headers
- โœ… listening
- โœ… error - ยตWS never throws errors
- โœ… wsClientError - ยตWS never throws errors

#### Server properties

- โœ… server.address()
- โœ… server.clients
- โœ… server.close(callback)
- โœ… server.shouldHandle(request)
- โŒ server.handleUpgrade(request, socket, head, callback) - see examples above for alternatives

### Client

This category only describes server clients. Client-side (`new ws.WebSocket()`) just uses original `ws` module, and therefore supports everything.

#### Client events

- โœ… close
- โœ… message
- โœ… ping
- โœ… pong
- โœ… dropped - this event only exists in Ultimate WS for handling dropped messages
- โœ… drain - this event only exists in Ultimate WS for handling backpressure draining
- โœ… error - ยตWS never throws errors

#### Client properties

- โœ… client.addEventListener(type, listener, options)
- โœ… client.binaryType
- โœ… client.bufferedAmount
- โœ… client.close(code, reason)
- โœ… client.isPaused
- โœ… client.extensions
- โœ… client.onclose
- โœ… client.onerror
- โœ… client.onmessage
- โœ… client.onopen
- โœ… client.pause()
- ๐Ÿšง client.ping()
- โŒ client.pong(data, mask, callback) - pongs are handled automatically, method does nothing
- โœ… client.protocol
- โœ… client.resume()
- โœ… client.readyState
- โœ… client.removeEventListener(type, listener)
- ๐Ÿšง client.send(data, options, callback) - returns 1 for success, 2 for dropped due to backpressure limit, 0 for built up backpressure that will drain over time. Callback will only get error if it returns 2.
- - โœ… options.binary
- - โœ… options.compress
- - โŒ options.fin
- - โŒ options.mask
- โœ… client.terminate()