Socket.io closed before receiving a handshake resp

2019-09-11 07:37发布

问题:

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!

回答1:

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. https://stackoverflow.com/a/31658307/2633577

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