npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ultimate-ws

v1.0.12

Published

The Ultimate WebSocket server. ws-compatible server, based on uWebSockets.

Downloads

215

Readme

Ultimate WS

The Ultimate WebSocket server. Ultimate WS is an extremely fast, drop-in replacement for ws module, with support for Ultimate Express http upgrades. It uses 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 instead

Node.js >= 16.0.0 npm Patreon

Installation

npm install ultimate-ws
  1. Replace ws with ultimate-ws in your code
  2. 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

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:

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:

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 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:

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:

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:

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:

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:

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 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 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()