Using node http-proxy to proxy websocket connections
Asked Answered
M

2

17

I have an application that uses websockets via socket.io. For my application I would like to use a separate HTTP server for serving the static content and JavaScript for my application. Therefore, I need to put a proxy in place.

I am using node-http-proxy. As a starting point I have my websockets app running on port 8081. I am using the following code to re-direct socket.io communications to this standalone server, while using express to serve the static content:

var http = require('http'),
    httpProxy = require('http-proxy'),
    express = require('express');

// create a server
var app = express();
var proxy = httpProxy.createProxyServer({ ws: true });

// proxy HTTP GET / POST
app.get('/socket.io/*', function(req, res) {
  console.log("proxying GET request", req.url);
  proxy.web(req, res, { target: 'http://localhost:8081'});
});
app.post('/socket.io/*', function(req, res) {
  console.log("proxying POST request", req.url);
  proxy.web(req, res, { target: 'http://localhost:8081'});
});

// Proxy websockets
app.on('upgrade', function (req, socket, head) {
  console.log("proxying upgrade request", req.url);
  proxy.ws(req, socket, head);
});

// serve static content
app.use('/', express.static(__dirname + "/public"));

app.listen(8080);

The above application works just fine, however, I can see that socket.io is no longer using websockets, it is instead falling back to XHR polling.

I can confirm that by looking at the logs from the proxy code:

proxying GET request /socket.io/1/?t=1391781619101
proxying GET request /socket.io/1/xhr-polling/f-VVzPcV-7_IKJJtl6VN?t=13917816294
proxying POST request /socket.io/1/xhr-polling/f-VVzPcV-7_IKJJtl6VN?t=1391781629
proxying GET request /socket.io/1/xhr-polling/f-VVzPcV-7_IKJJtl6VN?t=13917816294
proxying GET request /socket.io/1/xhr-polling/f-VVzPcV-7_IKJJtl6VN?t=13917816294

Does anyone know how to proxy the web sockets communication? All the examples from node-http-proxy assume that you want to proxy all traffic, rather than proxying some and serving others.

Muscarine answered 7/2, 2014 at 14:3 Comment(2)
Personally I use nginx both as http server and as proxy. It works very well for websocket proxying (that's the reason I switched from apache).Enthusiast
@dystroy thanks for the tip - although I was hoping for a node-based solution. This is so that developers can have a local set-up that reflects the live deployment.Muscarine
B
26

Just stumbled upon your question, and I see that it is still not answered. Well, in case you are still looking for the solution... The problem in your code is that app.listen(8080) is just syntactic sugar for

require('http').createServer(app).listen(8080)

while app itself is just a handler function, not an instance of httpServer (I personally believe that this feature should be removed from Express to avoid confusion). Thus, your app.on('upgrade') is actually never used. You should instead write

var server = require('http').createServer(app);
server.on('upgrade', function (req, socket, head) {
  proxy.ws(req, socket, head);
});
server.listen(8080);

Hope, that helps.

Bondstone answered 24/3, 2014 at 9:14 Comment(1)
this works perfectly! I've created a gist for a setup of browser-sync and express: gist.github.com/timaschew/00ba49d3a39bc3dfbe8fShape
D
-1

Do you need both servers? If not you could use the same server for static files and to listen for socket connections:

// make the http server
var express = require('express'),
    app = express(), server = require('http').createServer(app),
    io;

// serve static content
server.use('/', express.static(__dirname + '/public'));

server.listen(8080);

// listen for socket connections
io = require('socket.io').listen(server);

// socket stuff here
Dealate answered 7/2, 2014 at 16:19 Comment(1)
Yes, I know I could use one server - but the whole point is that in production the streaming connection will come from a different server! Thanks for your input :-)Muscarine

© 2022 - 2024 — McMap. All rights reserved.