Jorge Barreto
Jorge Barreto

Reputation: 188

NextJS, Express, Error during WebSocket handshake: Unexpected response code: 200

The basic problem can be summarized as follows: When creating a Websocket server in Node using ws with the server option populated by an express server(as in this example), while using that same express server to handle the routing for NextJS (as in this example), the upgrade header seems to not be properly parsed.

Instead of the request being routed to the Websocket server, express sends back an HTTP 200 OK response.

I've searched high and low for an answer to this, it may be that I simply do not understand the problem. A possibly related question was brought up in an issue on NextJS's github. They recommend setting WebsocketPort and WebsocketProxyPort options in the local next.config.js, however I have tried this to no avail.

A minimal example of the relevant server code can be found below. You may find the full example here.

const express = require('express')
const next = require('next')
const SocketServer = require('ws').Server;
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
    const server = express()

    server.all('*', (req, res) => {
    return handle(req, res)
    })

    server.listen(port, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${port}`)
    })

    const wss = new SocketServer({ server });

    wss.on('connection', function connection(ws, request) {
        console.log('Client connected');
        ws.on('close', () => console.log('Client disconnected'));
    });
    wss.on('error', function (error) {
        console.log(error);
    });

    setInterval(() => {
        wss.clients.forEach((client) => {
          client.send(new Date().toTimeString());
        });
      }, 1000);    

}).catch(ex => {
    console.error(ex.stack);
    process.exit(1);
});

The expected result, of course, is a connection to the websocket server. Instead I receive the following error:

WebSocket connection to 'ws://localhost:3000/' failed: Error during WebSocket handshake: Unexpected response code: 200

Can anyone elucidate anything for me here?

Upvotes: 1

Views: 3457

Answers (1)

Jorge Barreto
Jorge Barreto

Reputation: 188

Ok, after more digging I have solved the problem. Quite simply, the ws.Server object to which I was trying to feed the server = express() object is not strictly speaking an http server object. However, server.listen() returns such an http server object. On such an object we can listen for an 'upgrade' call, which we can pass to our ws.Server object's handleUpgrade() event listener, through which we can connect. I will be updating the examples that I linked in my question, but the relevant code is below:

app.prepare().then(() => {
    const server = express()

    server.all('*', (req, res) => {
    return handle(req, res)
    })

    const wss = new SocketServer({ server });

    wss.on('connection', function connection(ws, request) {
        console.log('Client connected');
        ws.on('close', () => console.log('Client disconnected'));
    });
    wss.on('error', function (error) {
        console.log(error);
    });

    let srv = server.listen(port, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${port}`)
    })

    srv.on('upgrade', function(req, socket, head) {
        wss.handleUpgrade(req, socket, head, function connected(ws) {
            wss.emit('connection', ws, req);
        })
    });

Upvotes: 1

Related Questions