jsdario jsdario - 6 months ago 460
Node.js Question

Socket.io closed before receiving a handshake response [ECONNRESET] after a proxy

I am programming a task manager for node.js apps. These apps are opened in an arbitrary port, so, in order to reach them I want to fake their root in
a URL like this:

http://domain.com/proxy/yourApplication


So third parties could make HTTP requests to their app without knowing in which port it runs.

Then I chose http-proxy:

var express = require('express')
, router = express.Router()
, Helper = require('../helper')
, launcher = require('../launcher')
, httpProxy = require('http-proxy')
, proxy = httpProxy.createProxyServer({ws: true});


And the code that listens at that route...

// Proxy
router.use('/proxy/:name?', function(req, res) {

var app, reqPath, referer, proxyUrl;

// Capture the referer to proxy the request
// in case the path is not clear enaugh
if (req.get('referer') !== undefined) {
var aux = req.get('referer').split('/');
referer = aux[aux.indexOf('proxy') + 1]
}

// This block returns an app object
// with the port where it is running
app = launcher.getApp(req.params.name)
|| launcher.getApp(referer);

if (app) {
// Here app is running

// In case the path is /proxy/:name
// instead of /proxy/:name/ you need this block
req.url = (req.url === '/') ? '' : req.url;
reqPath = (referer !== undefined)
? '/' + req.params.name + req.url
: req.url;
req.url = reqPath.replace('/proxy/', '/');
req.url = req.url.replace('/' + app.name, '');

// This block of code actually pipes the request
// to the running app and pass it to the client
proxyUrl = req.protocol + '://localhost:' + app.port;
proxy.web(req, res, {
target: proxyUrl
});

// Some logging
console.log('req url: %s', req.url);
console.log('proxy url: %s', proxyUrl);
console.log('referer: %s', referer);

} else {
// Here app is not running
res.status(404).json("App not running");
}
});


It works just fine with most apps, but when opening an app with socket.io it prompts:

WebSocket connection to 'ws://localhost/proxy/xy-terminal/socket.io/1/websocket/gMqK_XRwZENuUibi4ekJ' failed: Connection closed before receiving a handshake response


In the server console...

Trace: { [Error: socket hang up] code: 'ECONNRESET' }
at process.<anonymous> (/Users/jdario/Development/xy-dashboard/www:107:11)
at process.emit (events.js:107:17)
at process._fatalException (node.js:236:26)
at ProxyServer.emit (/Users/jdario/Development/xy-dashboard/node_modules/http-proxy/node_modules/eventemitter3/index.js:75:35)
at ClientRequest.proxyError (/Users/jdario/Development/xy-dashboard/node_modules/http-proxy/lib/http-proxy/passes/web-incoming.js:140:16)
at ClientRequest.emit (events.js:129:20)
at Socket.socketCloseListener (_http_client.js:247:9)
at Socket.emit (events.js:129:20)
at TCP.close (net.js:485:12)


Since it is a "proxy" that fakes an app root I may not be available to modify their source code individually, they should just work as intended. When opening them in the right port (3000 in this case) they do not show any error.

Thank you in advance!

Answer

Shortest answer was to indeed modify the proxied apps source code individually. So now they use

var io = require('socket.io')(http, { path: '/proxy/yourApplication'})

This post addressed the problem. http://stackoverflow.com/a/31658307/2633577

However this answer is not the best because it is not 100% transparent to hosted apps.