Skip to content

Conversation

@sudo-rgorai
Copy link

@sudo-rgorai sudo-rgorai commented Sep 12, 2025

Add new secure-ws library

Overview

Add secure-ws library to manage websocket connections for Node.js and TypeScript projects.

Motivation

  • To provide a reusable WebSocket provider for projects requiring real-time communication.
  • To enable easy integration with existing Express middleware and TypeScript-based codebases.

Resolution

Linked to getfundwave/discussions/issues/445
Closes getfundwave/discussions/issues/477

Features

  • Core WebSocket Provider:
    Implements a modular WebSocket provider in core/web-socket-provider.ts with support for custom protocols and extensibility.

  • Protocol Codec:
    Encapsulates WebSocket protocol encoding/decoding logic in core/ws-protocol-codec.ts to send headers and body in the websocket upgrade handshake

  • Middleware Support:
    Includes utilities for adapting Express-style middleware to WebSocket handlers in utils/middleware-adapter.ts and utils/inject-http-request.ts.

this.server.emit('connection', ws, request);
});
} else {
// socket.write('HTTP/1.1 404 Not Found\r\n\r\n');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a log here?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private routes: Record<string, AddRouteParams>;

constructor() {
this.server = new WebSocketServer({ noServer: true });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if we set noServer to false? Then we are able to set our own upgrade logic in the specified http server?

https://www.reddit.com/r/node/comments/sfgmum/can_someone_kindly_explain_what_noserver_mode/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If noServer is set to false (which it is by default), it spins up an http server on the specified port. In this case we won't have any control over the upgrade logic.

const wss = new WebSocketServer({ port: 8080 });

In our case, we already have an http server provided by express and we want to just use the socket connection after upgrade happens. Hence noServer.

The alternative is to pass the server to the constructor.

import { createServer } from 'https';
import { WebSocketServer } from 'ws';

const server = createServer({
  cert: readFileSync('/path/to/cert.pem'),
  key: readFileSync('/path/to/key.pem')
});
const wss = new WebSocketServer({ server });

But even in that case, this.server.handleUpgrade will be called automatically on all upgrades. We don't want this as we only want to upgrade if the enpoint matches a registered route [Ref lines 38-41 core/web-socket-provider.ts]

From docs:

One of port, server or noServer must be provided or an error is thrown.

Refs:

  1. https://www.npmjs.com/package/ws
  2. https://github.com/websockets/ws/blob/0a9621f9ff35e6f80c9c8471d0b202af4e357705/doc/ws.md#new-websocketserveroptions-callback

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the idea is to run the WS server in parallel with regular REST API? I'd imagine having a separate server for WS only might help plan scaling up and rate limiting better.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that depends on the number of ws endpoints one wants across services. If we want to have one endpoint for most of the services that would mean a common ws server would need access to all resources for those microservices. This ws server would become a monolith soon. For just a few endpoints, having a common ws makes sense for scalability but, in my opinion, the drawbacks would overcome the benefits as the number of endpoints increase.

The other alternative would be to have ws microservices in parallel with the existing microservices. But that would nearly double the number of microservices we have right now, leading to additional overhead of managing these services.

Another approach that was discussed previously was to have a common ws-service that only manages a single socket connection from the client and relays the request to the respective services. However it would still need ws endpoints on the services that it connects to. We can add rate limiting rules to this service and then later plug this between the client and server with the current architecture.

@@ -0,0 +1,16 @@
export class WSProtocolCodec {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather Base64UrlSafeCodec if it isn't specific to WS

Copy link
Author

@sudo-rgorai sudo-rgorai Sep 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is specific to the websocket as the name of the websocket protocol (Sec-WebSocket-Protocol) needs to be url safe

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants