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

ews-server

v1.0.7

Published

An abstraction layer built on top of uWebSockets.js to provide callbacks and authentication for Web Sockets

Downloads

15

Readme

enhancedWebSockets.js Server

You can view the client documentation here.

This is a small abstraction layer built on top of uWebSockets.js that provides simple socket authentication alongside message callbacks.

It provides the same API as uWebSockets.js, without the http server methods.

You can browse the uWebSockes.js documentation to get an understanding of what the different configuration options include.

This package extends the functionality of uWebSockets.js by adding an authentication handler alongside individual message events.

Example

import { App, AuthenticationError, SocketReplyError } from 'ews-server';

const app = App()
    .ws('/*', {
        // Both optional, allows whitelisting/blacklisting specific origins from
        // connecting. By default, all origins are allowed
        disallowedOrigins: [ 'https://app.local', 'http://socket.example.com' ],
        allowedOrigins: 'https://localhost:3000',

        // Socket authentication can be asynchronous or synchronous
        authentication(token) {
            // Perform your socket authentication here, based on the string
            // supplied on connection (perfect for JWT tokens)
            try {
                const { username, userID } = someDecodeFunction(token);
                return { username, userID };
            } catch {
                throw new AuthenticationError('Invalid token provided');
            }
            
            // Alternatively, if you wish to allow unauthenticated
            // sockets to connect, don't throw an AuthenticationError
        },
        
        open(socket) {
            const { id } = socket;
            const { userID } = socket.userData;
            
            if(userID)
                console.log(`User ${ userID } connected`);
            else console.log(`Guest connected`);
            
            // Send an event `hello` with data `world`
            app.send(id, 'hello', 'world');
        },
        
        close(socket) {
            const { userID } = socket.userData;
            
            if(userID)
                console.log(`User ${ userID } disconnected`);
            else console.log(`Guest disconnected`);
        },
        
        // You can either return a promise or explicitly call callback(err, data)
        // if you want to reply to the client.
        async message(socket, event, data, callback) {
            const { userID } = socket.userData;
            
            if(!userID)
                return callback('Socket message requires authentication');
                
            // If returning a promise and you want to reply with an error to the client,
            // throw a SocketReplyError. All other error typres will be unhandled. This is
            // to prevent errors from leaking through to the client.
            if(!data)
                throw new SocketReplyError('Socket message requires data payload');
                
            switch(event) {
                case 'it':
                    return 'worked';
                default:
                    throw new SocketReplyError('Unknown event');
            }
        }
    })
    .listen(9001, socket => {
        if(socket)
            console.log('Listening on port 9001');
    });

Key differences to uWebSockets.js

Origin blacklisting/whitelisting

Since WebSockets don't have the ability to provide CORS headers to the browser, origin verification has to happen on the server.

By default, connections from all origins are allowed. If you'd like to operate a whitelist, provide allowed origins to allowedOrigins in the ws() object. If you'd like to operate a blacklist, provide disallowed origins to disallowedOrigins in the ws() object.

You can use both if you would like to, but it is redundant. You can either pass a single domain as a string or an array of domains.

Origin verification is performed prior to the authentication handler being called.

Authentication handler

This library supports an authentication string being sent with the initial WebSocket upgrade request, by using the sec-websocket-protocol header. By sending the authentication string in this header you prevent it from being leaked in the url.

You can choose to handle authentication the following ways:

  • Provide no authentication method and allow all clients to connect
  • Provide an authentication method and throw an AuthenticationError if authentication fails. This dispatches the error message to the client and immediately terminates the connection.
  • Provide an authentication method and don't throw an AuthenticationError. This allows unauthenticated clients to connect to the socket.

The authentication handler can either be synchronous or asynchronous. Whatever you return from the handler will be set to socket.userData for all future handlers (open, close, message).

If identifying a user, common practice would be to return { userID } and access this through socket.userData.userID in other handlers. If the socket isn't authenticated, you could return false or nothing at all to differentiate between authenticated and unauthenticated sockets.

Socket messages

Clients can optionally specify a callback function that allows the server to provide a response to individual events. Under the hood, this library treats all events as though they have provided a callback so that your code doesn't break if a malicious or broken client doesn't send a callback.

Messages also contain named events, which are accessed through the following:

message(socket, event[, data[, callback]]) {
    switch(event) {
        case ...:
            break;
    }
}

You must check if callback is defined prior to invoking the callback or returning a SocketReplyError.

You can either return a promise to trigger the callback handler or invoke the callback function yourself. If you wish to reply an error, either call callback(error) or throw a SocketReplyError with the message as the first argument.

Any errors thrown that aren't an instance of SocketReplyError will not be handled by the library and not sent to the client.

Sending data to clients

All incoming and outgoing data is treated as JSON, and is encoded/decoded by the library. You are able to send data alongside the event name, which is as simple as app.send(socketID, event[, data]).

In the future it will be possible to use socket.send(event, data). Callbacks are not supported when emitting events to the client.

Publishing messages to multiple clients

uWebSockets.js supports subscribing to topics, so you are able to call socket.subscribe('topic'). You can then call app.publish(topic, event[, data ]) to send a message to all subscribed clients.