forked from coolaj86/goldilocks.js
		
	cleaned up all of the custom HTTP handling logic
This commit is contained in:
		
							parent
							
								
									ab31bae6ff
								
							
						
					
					
						commit
						ab011d1829
					
				@ -21,108 +21,6 @@ module.exports.create = function (deps, config) {
 | 
			
		||||
  var tls = require('tls');
 | 
			
		||||
  var domainMatches = require('./match-domain').match;
 | 
			
		||||
 | 
			
		||||
  var tcpRouter = {
 | 
			
		||||
    _map: { }
 | 
			
		||||
  , _create: function (address, port) {
 | 
			
		||||
      // port provides hinting for http, smtp, etc
 | 
			
		||||
      return function (conn, firstChunk, opts) {
 | 
			
		||||
        console.log('[tcpRouter] ' + address + ':' + port + ' ' + (opts.servername || ''));
 | 
			
		||||
 | 
			
		||||
        var m;
 | 
			
		||||
        var str;
 | 
			
		||||
        var hostname;
 | 
			
		||||
        var newHeads;
 | 
			
		||||
 | 
			
		||||
        // TODO test per-module
 | 
			
		||||
        // Maybe HTTP
 | 
			
		||||
        if (firstChunk[0] > 32 && firstChunk[0] < 127) {
 | 
			
		||||
          str = firstChunk.toString();
 | 
			
		||||
          m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
 | 
			
		||||
          hostname = (m && m[1].toLowerCase() || '').split(':')[0];
 | 
			
		||||
          console.log('[tcpRouter] hostname', hostname);
 | 
			
		||||
          if (/HTTP\//i.test(str)) {
 | 
			
		||||
            //conn.__service = 'http';
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!hostname) {
 | 
			
		||||
          // TODO allow tcp tunneling
 | 
			
		||||
          // TODO we need some way of tagging tcp as either terminated tls or insecure
 | 
			
		||||
          conn.write(
 | 
			
		||||
            "HTTP/1.1 404 Not Found\r\n"
 | 
			
		||||
          + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
 | 
			
		||||
          + "Content-Type: text/html\r\n"
 | 
			
		||||
          + "Content-Length: " + 9 + "\r\n"
 | 
			
		||||
          + "\r\n"
 | 
			
		||||
          + "Not Found"
 | 
			
		||||
          );
 | 
			
		||||
          conn.end();
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Poor-man's http proxy
 | 
			
		||||
        // XXX SECURITY XXX: should strip existing X-Forwarded headers
 | 
			
		||||
        newHeads =
 | 
			
		||||
          [ "X-Forwarded-Proto: " + (opts.encrypted ? 'https' : 'http')
 | 
			
		||||
          , "X-Forwarded-For: " + (opts.remoteAddress || conn.remoteAddress)
 | 
			
		||||
          , "X-Forwarded-Host: " + hostname
 | 
			
		||||
          ];
 | 
			
		||||
 | 
			
		||||
        if (!opts.encrypted) {
 | 
			
		||||
          // a exists-only header that a bad client could not remove
 | 
			
		||||
          newHeads.push("X-Not-Encrypted: yes");
 | 
			
		||||
        }
 | 
			
		||||
        if (opts.servername) {
 | 
			
		||||
          newHeads.push("X-Forwarded-Sni: " + opts.servername);
 | 
			
		||||
          if (opts.servername !== hostname) {
 | 
			
		||||
            // an exists-only header that a bad client could not remove
 | 
			
		||||
            newHeads.push("X-Two-Servernames: yes");
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        firstChunk = firstChunk.toString('utf8');
 | 
			
		||||
        // JSON.stringify("Host: example.com\r\nNext: Header".replace(/(Host: [^\r\n]*)/i, "$1" + "\r\n" + "X: XYZ"))
 | 
			
		||||
        firstChunk = firstChunk.replace(/(Host: [^\r\n]*)/i, "$1" + "\r\n" + newHeads.join("\r\n"));
 | 
			
		||||
 | 
			
		||||
        process.nextTick(function () {
 | 
			
		||||
          conn.unshift(Buffer.from(firstChunk, 'utf8'));
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        //
 | 
			
		||||
        // hard-coded routes for the admin interface
 | 
			
		||||
        if (
 | 
			
		||||
          /\blocalhost\.admin\./.test(hostname) || /\badmin\.localhost\./.test(hostname)
 | 
			
		||||
          || /\blocalhost\.alpha\./.test(hostname) || /\balpha\.localhost\./.test(hostname)
 | 
			
		||||
        ) {
 | 
			
		||||
          if (!modules.admin) {
 | 
			
		||||
            modules.admin = require('./modules/admin.js').create(deps, config);
 | 
			
		||||
          }
 | 
			
		||||
          modules.admin.emit('connection', conn);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO static file handiling and such or whatever
 | 
			
		||||
        if (!modules.http) {
 | 
			
		||||
          modules.http = require('./modules/http.js').create(deps, config);
 | 
			
		||||
        }
 | 
			
		||||
        opts.hostname = hostname;
 | 
			
		||||
        conn.__opts = opts;
 | 
			
		||||
 | 
			
		||||
        modules.http.emit('connection', conn);
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
  , get: function getTcpRouter(address, port) {
 | 
			
		||||
      address = address || '0.0.0.0';
 | 
			
		||||
 | 
			
		||||
      var id = address + ':' + port;
 | 
			
		||||
      if (!tcpRouter._map[id]) {
 | 
			
		||||
        tcpRouter._map[id] = tcpRouter._create(address, port);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return tcpRouter._map[id];
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
  var tlsRouter = {
 | 
			
		||||
    proxy: function (socket, opts, mod) {
 | 
			
		||||
      var newConn = deps.net.createConnection({
 | 
			
		||||
@ -231,25 +129,36 @@ module.exports.create = function (deps, config) {
 | 
			
		||||
 | 
			
		||||
    // TLS byte 1 is handshake and byte 6 is client hello
 | 
			
		||||
    if (0x16 === firstChunk[0]/* && 0x01 === firstChunk[5]*/) {
 | 
			
		||||
      console.log('tryTls');
 | 
			
		||||
      opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid';
 | 
			
		||||
      tlsRouter.processSocket(conn, firstChunk, opts);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.log('tryTcp');
 | 
			
		||||
 | 
			
		||||
    if (opts.hyperPeek) {
 | 
			
		||||
      // even though we've already peeked, this logic is just as well to let be
 | 
			
		||||
      // since it works properly either way, unlike the tls socket
 | 
			
		||||
      conn.once('data', function (chunk) {
 | 
			
		||||
        console.log('hyperPeek re-peek data', chunk.toString('utf8'));
 | 
			
		||||
        tcpRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, chunk, opts);
 | 
			
		||||
    // This doesn't work with TLS, but now that we know this isn't a TLS connection we can
 | 
			
		||||
    // unshift the first chunk back onto the connection for future use. The unshift should
 | 
			
		||||
    // happen after any listeners are attached to it but before any new data comes in.
 | 
			
		||||
    if (!opts.hyperPeek) {
 | 
			
		||||
      process.nextTick(function () {
 | 
			
		||||
        conn.unshift(firstChunk);
 | 
			
		||||
      });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tcpRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, firstChunk, opts);
 | 
			
		||||
    // Connection is not TLS, check for HTTP next.
 | 
			
		||||
    if (firstChunk[0] > 32 && firstChunk[0] < 127) {
 | 
			
		||||
      var firstStr = firstChunk.toString();
 | 
			
		||||
      if (/HTTP\//i.test(firstStr)) {
 | 
			
		||||
        if (!modules.http) {
 | 
			
		||||
          modules.http = require('./modules/http.js').create(deps, config);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        conn.__opts = opts;
 | 
			
		||||
        modules.http.emit('connection', conn);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    console.warn('failed to identify protocol from first chunk', firstChunk);
 | 
			
		||||
    conn.close();
 | 
			
		||||
  }
 | 
			
		||||
  function netHandler(conn, opts) {
 | 
			
		||||
    opts = opts || {};
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,5 @@ module.exports.create = function (deps, conf) {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  /* device, addresses, cwd, http */
 | 
			
		||||
  var app = require('../app.js')(deps, conf, opts);
 | 
			
		||||
  var http = require('http');
 | 
			
		||||
  return http.createServer(app);
 | 
			
		||||
  return require('../app.js')(deps, conf, opts);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,16 @@
 | 
			
		||||
 | 
			
		||||
module.exports.create = function (deps, conf) {
 | 
			
		||||
  var app = require('express')();
 | 
			
		||||
  var adminApp = require('./admin').create(deps, conf);
 | 
			
		||||
  var domainMatches = require('../match-domain').match;
 | 
			
		||||
 | 
			
		||||
  var adminDomains = [
 | 
			
		||||
    /\blocalhost\.admin\./
 | 
			
		||||
  , /\blocalhost\.alpha\./
 | 
			
		||||
  , /\badmin\.localhost\./
 | 
			
		||||
  , /\balpha\.localhost\./
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  // We handle both HTTPS and HTTP traffic on the same ports, and we want to redirect
 | 
			
		||||
  // any unencrypted requests to the same port they came from unless it came in on
 | 
			
		||||
  // the default HTTP port, in which case there wont be a port specified in the host.
 | 
			
		||||
@ -17,6 +25,18 @@ module.exports.create = function (deps, conf) {
 | 
			
		||||
    redirecter(req, res, next);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function handleAdmin(req, res, next) {
 | 
			
		||||
    var admin = adminDomains.some(function (re) {
 | 
			
		||||
      return re.test(req.headers.host);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (admin) {
 | 
			
		||||
      adminApp(req, res);
 | 
			
		||||
    } else {
 | 
			
		||||
      next();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function respond404(req, res) {
 | 
			
		||||
    res.writeHead(404);
 | 
			
		||||
    res.end('Not Found');
 | 
			
		||||
@ -36,6 +56,14 @@ module.exports.create = function (deps, conf) {
 | 
			
		||||
    , toProxy: true
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // We want to override the default value for some headers with the extra information we
 | 
			
		||||
    // have available to us in the opts object attached to the connection.
 | 
			
		||||
    proxy.on('proxyReq', function (proxyReq, req) {
 | 
			
		||||
      var conn = req.connection;
 | 
			
		||||
      var opts = conn.__opts;
 | 
			
		||||
      proxyReq.setHeader('X-Forwarded-For', opts.remoteAddress || conn.remoteAddress);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return function (req, res, next) {
 | 
			
		||||
      var hostname = req.headers.host.split(':')[0];
 | 
			
		||||
      var relevant = mod.domains.some(function (pattern) {
 | 
			
		||||
@ -51,6 +79,7 @@ module.exports.create = function (deps, conf) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  app.use(redirectHttps);
 | 
			
		||||
  app.use(handleAdmin);
 | 
			
		||||
 | 
			
		||||
  (conf.http.modules || []).forEach(function (mod) {
 | 
			
		||||
    if (mod.name === 'proxy') {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user