Socket.io-based app running through node proxy server disconnecting all sockets whenever one disconnects

node-http-proxy, node.js, socket.io, sockets

Solution

I think your intuition about needing to "carry the socket traffic through" the proxy server is right on. To establish a websocket, the client makes an HTTP request with a special `Upgrade` header, signalling the server to switch protocols (RFC 6455). In node, `http.Server` instances emit an `upgrade` event when this happens and if the event is not handled, the connection is immediately closed.

You need to listen for the `upgrade` event on your http server and handle it:

var proxy = httpProxy.createProxyServer({ws: true})
var http = http.createServer(/* snip */).listen(80)

// handle upgrade events by proxying websockets
// something like this
http.on('upgrade', function (req, socket, head) {
  proxy.ws(req, socket, head, {target:sites(req.headers.host)})
})

See the node docs on the upgrade event and the node-http-proxy docs for more.

Problem

I made a basic chat app using `node.js`, `express` and `socket.io`. It's not too different from the tutorial chat app for socket.io, it simply emits events between connected clients. When I ran it on port 3001 on my server, it worked fine. Then I made a proxy server app using `node-http-proxy` which listens on port 80 and redirects traffic based on the requested url to various independent node apps I have running on different ports. Pretty straightforward. But something is breaking. Whenever anyone disconnects, every single socket dis- and re-connects. This is bad for my chat app, which has connection-based events. The client consoles all show: WebSocket connection to 'ws://[some socket info]' failed: Connection closed before receiving a handshake response Here's what I think are the important parts of my code. proxy-server.js ``` var http = require('http'); var httpProxy = require('http-proxy'); //create proxy template object with websockets enabled var proxy = httpProxy.createProxyServer({ws: true}); //check the header on request and return the appropriate port to proxy to function sites (req) { //webapps get their own dedicated port if (req == 'mychatwebsite.com') {return 'http://localhost:3001';} else if (req == 'someothersite.com') {return 'http://localhost:3002';} //static sites are handled by a vhost server on port 3000 else {return 'http://localhost:3000';} } //create node server on port 80 and proxy to ports accordingly http.createServer(function (req, res) { proxy.web(req, res, { target: sites(req.headers.host) }); }).listen(80); ``` chat-app.js ``` /* ...other modules */ var express = require("express"); var app = exports.app = express(); //I probably don't need "exports.app" anymore var http = require("http").Server(app); var io = require("socket.io")(http); io.on("connection", function (socket) { /* ...fun socket.on and io.emit stuff */ socket.on("disconnect", function () { //say bye }); }); http.listen(3001, function () { console.log("listening on port 3001"); }); ``` Now from what I've read on socket.io's site, I might need to use something to carry the socket traffic through my proxy server. I thought that node-http-proxy did that for me with the `{ws: true}` option as it states in their docs, but apparently it doesn't work like I thought it would. socket.io mentions three different things: - `sticky session` based on node's built in `cluster` module - `socket.io-redis`, which allows separate socket.io instances to talk to each other - `socket.io-emitter`, which allows socket.io to talk to non-socket.io processes I have exactly no idea what any of this means or does. I am accidentally coding way above my skill level here, and I have no idea which of these tools will solve my problem (if any) or even what the cause of my problem really is. Obligatory apology: I'm new to node.js, so please forgive me. Also obligatory: I know other apps like nginx can solve a lot of my issues, but my goal is to learn and understand how to use this set of tools before I go picking up new ones. And, the less apps I use, the better.

Original source