From ab31bae6ffe4ecd5c0bc4a20869363eaf21d2419 Mon Sep 17 00:00:00 2001 From: tigerbot Date: Tue, 9 May 2017 14:16:21 -0600 Subject: [PATCH] implemented more dynamic HTTP proxying --- lib/goldilocks.js | 17 +-------- lib/match-domain.js | 17 +++++++++ lib/modules/http.js | 84 +++++++++++++++++++++++++++++++-------------- package.json | 1 + 4 files changed, 77 insertions(+), 42 deletions(-) create mode 100644 lib/match-domain.js diff --git a/lib/goldilocks.js b/lib/goldilocks.js index 0c2248f..013a4cb 100644 --- a/lib/goldilocks.js +++ b/lib/goldilocks.js @@ -19,22 +19,7 @@ module.exports.create = function (deps, config) { var secureContexts = {}; var tunnelAdminTlsOpts = {}; var tls = require('tls'); - - function domainMatches(pattern, servername) { - // Everything matches '*' - if (pattern === '*') { - return true; - } - - if (/^\*./.test(pattern)) { - // get rid of the leading "*." to more easily check the servername against it - pattern = pattern.slice(2); - return pattern === servername.slice(-pattern.length); - } - - // pattern doesn't contains any wildcards, so exact match is required - return pattern === servername; - } + var domainMatches = require('./match-domain').match; var tcpRouter = { _map: { } diff --git a/lib/match-domain.js b/lib/match-domain.js new file mode 100644 index 0000000..e4a8a1d --- /dev/null +++ b/lib/match-domain.js @@ -0,0 +1,17 @@ +'use strict'; + +module.exports.match = function (pattern, servername) { + // Everything matches '*' + if (pattern === '*') { + return true; + } + + if (/^\*./.test(pattern)) { + // get rid of the leading "*." to more easily check the servername against it + pattern = pattern.slice(2); + return pattern === servername.slice(-pattern.length); + } + + // pattern doesn't contains any wildcards, so exact match is required + return pattern === servername; +}; diff --git a/lib/modules/http.js b/lib/modules/http.js index 3631cd5..1b8207e 100644 --- a/lib/modules/http.js +++ b/lib/modules/http.js @@ -1,34 +1,66 @@ 'use strict'; module.exports.create = function (deps, conf) { - // This should be able to handle things like default web path (i.e. /srv/www/hostname), - // no-www redirect, and transpilation of static assets (i.e. cached versions of raw html) - // but right now it's a very dumb proxy + var app = require('express')(); + var domainMatches = require('../match-domain').match; - function createConnection(conn) { - var opts = conn.__opts; - var newConn = deps.net.createConnection({ - port: conf.http.proxy.port - , host: '127.0.0.1' - - , servername: opts.servername - , data: opts.data - , remoteFamily: opts.family || conn.remoteFamily - , remoteAddress: opts.address || conn.remoteAddress - , remotePort: opts.port || conn.remotePort - }, function () { - //console.log("[=>] first packet from tunneler to '" + cid + "' as '" + opts.service + "'", opts.data.byteLength); - // this will happen before 'data' is triggered - //newConn.write(opts.data); - }); - - newConn.pipe(conn); - conn.pipe(newConn); + // 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. + var redirecters = {}; + function redirectHttps(req, res, next) { + var port = req.headers.host.split(':')[1]; + var redirecter = redirecters[port]; + if (!redirecter) { + redirecter = redirecters[port] = require('redirect-https')({port: port}); + } + redirecter(req, res, next); } - return { - emit: function (type, conn) { - createConnection(conn); + function respond404(req, res) { + res.writeHead(404); + res.end('Not Found'); + } + + function createProxyRoute(mod) { + // This is the easiest way to override the createConnections function the proxy + // module uses, but take note the since we don't have control over where this is + // called the extra options availabled will be different. + var agent = new require('http').Agent({}); + agent.createConnection = deps.net.createConnection; + + var proxy = require('http-proxy').createProxyServer({ + agent: agent + , target: 'http://' + mod.address + , xfwd: true + , toProxy: true + }); + + return function (req, res, next) { + var hostname = req.headers.host.split(':')[0]; + var relevant = mod.domains.some(function (pattern) { + return domainMatches(pattern, hostname); + }); + + if (relevant) { + proxy.web(req, res); + } else { + next(); + } + }; + } + + app.use(redirectHttps); + + (conf.http.modules || []).forEach(function (mod) { + if (mod.name === 'proxy') { + app.use(createProxyRoute(mod)); } - }; + else { + console.warn('unknown HTTP module', mod); + } + }); + + app.use(respond404); + return require('http').createServer(app); }; diff --git a/package.json b/package.json index 197b0d4..6202868 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "finalhandler": "^0.4.0", "greenlock": "git+https://git.daplie.com/Daplie/node-greenlock.git#master", "greenlock-express": "git+https://git.daplie.com/Daplie/greenlock-express.git#master", + "http-proxy": "^1.16.2", "httpolyglot": "^0.1.1", "ipaddr.js": "git+https://github.com/whitequark/ipaddr.js.git#v1.3.0", "ipify": "^1.1.0",