1

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); // }); 

1 Answer 1

5

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.