gravity-ui / expresskit

MIT License
4 stars 2 forks source link

WebSockets support #37

Open vrozaev opened 3 weeks ago

vrozaev commented 3 weeks ago

Could you add support of WebSockets please?

I see it as an ability to create a specified route and handler.

resure commented 3 weeks ago

We'll probably add support to expresskit itself later, but in the meanwhile you can try to setup websocket connections on your app's side, just like with ordinary express.js servers.

Basically, you need to:

  1. Register WS server
  2. Receive upgrade request (handle it as a regular GET request: check auth etc.)
  3. Perform upgrade to websocket

Some code samples:

// Entrypoint to application

// ...

const app = new ExpressKit(nodekit, {
    'GET /ws': {
        handler: (req, res) => {
            res.websocket('main', (ws) => {
                req.ctx.log('Client connected');

                ws.send('hey there');

                ws.on('message', (message) => {
                    req.ctx.log('Got message', {message});
                    ws.send('got your message');
                });
            });
        },
    }
});

// Registering ws server
const wss = new WebSocket.Server({noServer: true});

// Handling upgrade request
app.httpServer.on(
    'upgrade',
    (req: http.IncomingMessage, socket: net.Socket, upgradeHead: Buffer) => {
        const res = new http.ServerResponse(req) as Response;
        res.assignSocket(socket);

        const head = Buffer.alloc(upgradeHead.length);
        upgradeHead.copy(head);

        res.websocket = (wssName: string, cb: Function) => {
            wss.handleUpgrade(req, socket, head, (client) => {
                wss.emit('connection', client, req);
                if (cb) {
                    cb(client);
                }
            });
        };

        return this.expressApp(req, res);
    },
);
vrozaev commented 3 weeks ago

Amazing, I make it work!

One note though: I replaced this.expressApp(req, res); with return app.express(req, res);.

So the working code is:

// Entrypoint to application

// ...

const app = new ExpressKit(nodekit, {
    'GET /ws': {
        handler: (req, res) => {
            res.websocket('main', (ws) => {
                req.ctx.log('Client connected');

                ws.send('hey there');

                ws.on('message', (message) => {
                    req.ctx.log('Got message', {message});
                    ws.send('got your message');
                });
            });
        },
    }
});

// Registering ws server
const wss = new WebSocket.Server({noServer: true});

// Handling upgrade request
app.httpServer.on(
    'upgrade',
    (req: http.IncomingMessage, socket: net.Socket, upgradeHead: Buffer) => {
        const res = new http.ServerResponse(req) as Response;
        res.assignSocket(socket);

        const head = Buffer.alloc(upgradeHead.length);
        upgradeHead.copy(head);

        res.websocket = (wssName: string, cb: Function) => {
            wss.handleUpgrade(req, socket, head, (client) => {
                wss.emit('connection', client, req);
                if (cb) {
                    cb(client);
                }
            });
        };

        return app.express(req, res);
    },
);