Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 240
Danneh Avatar asked Sep 28 '22 17:09

Danneh


1 Answers

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.

like image 130
Luke Vivier Avatar answered Oct 06 '22 20:10

Luke Vivier