Reputation: 13
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
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:
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);
Or manually attach an upgrade
listener to your http.Server
and call wss.handleUpgrade
.
Upvotes: 5