Ranko
Ranko

Reputation: 13

WebSockets WS error during WebSocket handshake: Unexpected response code: 200

trying to deploy my app with ws on Heroku. On my local machine everything worked out well but after deploying on Heroku I get a handshake error with response status 200

Here's my server code:

const WebSocketServer = require('ws').Server;
const moment = require('moment');
const app = require('./app');

const wss = new WebSocketServer({ app });

const connections = new Set();

wss.on('connection', (ws) => {
  connections.add(ws);

  ws.on('message', (message) => {
    const time = moment(new Date()).format('HH:mm');
    const messageData = {
      time,
      message,
    };
    for (const connection of connections) {
      connection.send(JSON.stringify(messageData));
    }
  });

  ws.on('close', () => {
    connections.delete(ws);
  });
 });

And my client code:

const HOST = location.origin.replace(/^http/, 'ws');
const ws = new WebSocket(HOST);

ws.onmessage = (e) => {
  //
};

messageForm.addEventListener('submit', (e) => {
  //
  ws.send(message);
  //
});

Upvotes: 1

Views: 11342

Answers (1)

josh3736
josh3736

Reputation: 144822

The ws Server constructor does not accept an app option, so your wss isn't actually listening to anything. If this exact code did work locally, it's hard to see how.

Since you're attempting to connect to a WebSocket that shares the same path as your page, the express app ends up handling the WebSocket client request and responds with your page HTML.

Your HTTP exchange ends up looking something like this — first the browser loads the page:

GET /page HTTP/1.1
Accept: text/html
…

HTTP/1.1 200 OK
Content-Type: text/html

<html> …

Then the page makes a new WebSocket(…), resulting in a WebSocket upgrade request:

GET /page HTTP/1.1
Connection: Upgrade
Upgrade: websocket
…

HTTP/1.1 200 OK
Content-Type: text/html

<html> …

You should see why this would fail: that's not a WebSocket response. Express thinks the request is a regular HTTP GET request and responds with your page's HTML. The browser's WebSocket implementation sees a response that does not conform to the WebSocket spec and rightly throws an error. It reports "status 200" because the server did respond with a HTTP 200 status code — even though it was an accident.

The correct way to attach a ws Server to a HTTP server is to either:

  1. Pass in the http.Server (not the express app) to ws.Server's server option

    var app = express(),
        server = http.createServer(app),
        wss = new ws.Server({ server });
    
    server.listen(process.env.PORT);
    
  2. Or manually attach an upgrade listener to your http.Server and call wss.handleUpgrade.

Upvotes: 5

Related Questions