Skip to content
GitLab
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • N node-http-proxy
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 482
    • Issues 482
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 102
    • Merge requests 102
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • http ... PARTY!
  • node-http-proxy
  • Issues
  • #757
Closed
Open
Issue created Dec 21, 2014 by Administrator@rootContributor

Random Web Socket Disconnects

Created by: goldfire

I've run into a strange issue with websocket proxying that has had me pulling me hair out for several weeks. Everything had been running smoothly on 1.5.1, but many users starting reporting disconnect problems after upgrading to 1.5.3 1.5.2. Clearly something in one of the two following commits is the issue, but I'm not sure if the cause is http-proxy or my implementation.

https://github.com/nodejitsu/node-http-proxy/commit/bcd8a564a802512c90df20353ca341a1d8c84501 https://github.com/nodejitsu/node-http-proxy/commit/c62766391e54069c7cf82f0135536aeabad7cd90

What has made it exceptionally frustrating is that I haven't been able to reproduce the problem, nor have I been able to find anything in commone with the users that reported the issue happening. It also didn't happen 100% of the time for those users, it seemed to come in bursts. If anyone even had any clues of where to keep looking, that would be a huge help as I've run out of ideas.

var http = require('https');
var fs = require('fs');
var proxy = require('http-proxy');
var request = require('request');

http.globalAgent.maxSockets = 10240;

// Define the servers to load balance.
var servers = [
  {host: 'SERVER1-IP', port: 80},
  {host: 'SERVER2-IP', port: 80},
  {host: 'SERVER3-IP', port: 80}
];
var failoverTimer = [];

// load the SSL cert
var ca = [
  fs.readFileSync('./certs/PositiveSSLCA2.crt'),
  fs.readFileSync('./certs/AddTrustExternalCARoot.crt')
];
var opts = {
  ca: ca,
  key: fs.readFileSync('./certs/example_wild.key'),
  cert: fs.readFileSync('./certs/STAR_example_com.crt')
};

// Create a proxy object for each target.
var proxies = servers.map(function (target) {
  return new proxy.createProxyServer({
    target: target,
    ws: true,
    xfwd: true,
    ssl: opts,
    down: false
  });
});

/**
 * Select a random server to proxy to. If a 'server' cookie is set, use that
 * as the sticky session so the user stays on the same server (good for ws fallbacks).
 * @param  {Object} req HTTP request data
 * @param  {Object} res HTTP response
 * @return {Number}     Index of the proxy to use.
 */
var selectServer = function(req, res) {
  var index = -1;
  var i = 0;

  // Check if there are any cookies.
  if (req.headers && req.headers.cookie && req.headers.cookie.length > 1) {
    var cookies = req.headers.cookie.split('; ');

    for (i=0; i<cookies.length; i++) {
      if (cookies[i].indexOf('server=') === 0) {
        var value = cookies[i].substring(7, cookies[i].length);
        if (value && value !== '') {
          index = value;
          break;
        }
      }
    }
  }

  // Select a random server if they don't have a sticky session.
  if (index < 0 || !proxies[index]) {
    index = Math.floor(Math.random() * proxies.length);
  }

  // If the selected server is down, select one that isn't down.
  if (proxies[index].options.down) {
    index = -1;

    var tries = 0;
    while (tries < 5 && index < 0) {
      var randIndex = Math.floor(Math.random() * proxies.length);
      if (!proxies[randIndex].options.down) {
        index = randIndex;
      }

      tries++;
    }
  }

  index = index >= 0 ? index : 0;

  // Store the server index as a sticky session.
  if (res) {
    res.setHeader('Set-Cookie', 'server=' + index + '; path=/');
  }

  return index;
};

/**
 * Fired when there is an error with a request.
 * Sets up a 10-second interval to ping the host until it is back online.
 * There is a 10-second buffer before requests start getting blocked to this host.
 * @param  {Number} index Index in the proxies array.
 */
var startFailoverTimer = function(index) {
  if (failoverTimer[index]) {
    return;
  }

  failoverTimer[index] = setTimeout(function() {
    // Check if the server is up or not
    request({
      url: 'http://' + proxies[index].options.target.host + ':' + proxies[index].options.target.port,
      method: 'HEAD',
      timeout: 10000
    }, function(err, res, body) {
      failoverTimer[index] = null;

      if (res && res.statusCode === 200) {
        proxies[index].options.down = false;
        console.log('Server #' + index + ' is back up.');
      } else {
        proxies[index].options.down = true;
        startFailoverTimer(index);
        console.log('Server #' + index + ' is still down.');
      }
    });
  }, 10000);
};

// Select the next server and send the http request.
var serverCallback = function(req, res) {
  var proxyIndex = selectServer(req, res);
  var proxy = proxies[proxyIndex];
  proxy.web(req, res);

  proxy.on('error', function(err) {
    startFailoverTimer(proxyIndex);
  });
};
var server = http.createServer(opts, serverCallback);

// Get the next server and send the upgrade request.
server.on('upgrade', function(req, socket, head) {
  var proxyIndex = selectServer(req);
  var proxy = proxies[proxyIndex];
  proxy.ws(req, socket, head);

  proxy.on('error', function(err, req, socket) {
    socket.end();
    startFailoverTimer(proxyIndex);
  });
});

server.listen(443);
Assignee
Assign to
Time tracking