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 tls = require('tls');
|
||||||
var domainMatches = require('./match-domain').match;
|
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 = {
|
var tlsRouter = {
|
||||||
proxy: function (socket, opts, mod) {
|
proxy: function (socket, opts, mod) {
|
||||||
var newConn = deps.net.createConnection({
|
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
|
// TLS byte 1 is handshake and byte 6 is client hello
|
||||||
if (0x16 === firstChunk[0]/* && 0x01 === firstChunk[5]*/) {
|
if (0x16 === firstChunk[0]/* && 0x01 === firstChunk[5]*/) {
|
||||||
console.log('tryTls');
|
|
||||||
opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid';
|
opts.servername = (parseSni(firstChunk)||'').toLowerCase() || 'localhost.invalid';
|
||||||
tlsRouter.processSocket(conn, firstChunk, opts);
|
tlsRouter.processSocket(conn, firstChunk, opts);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('tryTcp');
|
// 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
|
||||||
if (opts.hyperPeek) {
|
// happen after any listeners are attached to it but before any new data comes in.
|
||||||
// even though we've already peeked, this logic is just as well to let be
|
if (!opts.hyperPeek) {
|
||||||
// since it works properly either way, unlike the tls socket
|
process.nextTick(function () {
|
||||||
conn.once('data', function (chunk) {
|
conn.unshift(firstChunk);
|
||||||
console.log('hyperPeek re-peek data', chunk.toString('utf8'));
|
|
||||||
tcpRouter.get(opts.localAddress || conn.localAddress, conn.localPort)(conn, chunk, opts);
|
|
||||||
});
|
});
|
||||||
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) {
|
function netHandler(conn, opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
|
|
@ -60,7 +60,5 @@ module.exports.create = function (deps, conf) {
|
||||||
});
|
});
|
||||||
|
|
||||||
/* device, addresses, cwd, http */
|
/* device, addresses, cwd, http */
|
||||||
var app = require('../app.js')(deps, conf, opts);
|
return require('../app.js')(deps, conf, opts);
|
||||||
var http = require('http');
|
|
||||||
return http.createServer(app);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,8 +2,16 @@
|
||||||
|
|
||||||
module.exports.create = function (deps, conf) {
|
module.exports.create = function (deps, conf) {
|
||||||
var app = require('express')();
|
var app = require('express')();
|
||||||
|
var adminApp = require('./admin').create(deps, conf);
|
||||||
var domainMatches = require('../match-domain').match;
|
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
|
// 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
|
// 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.
|
// 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);
|
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) {
|
function respond404(req, res) {
|
||||||
res.writeHead(404);
|
res.writeHead(404);
|
||||||
res.end('Not Found');
|
res.end('Not Found');
|
||||||
|
@ -36,6 +56,14 @@ module.exports.create = function (deps, conf) {
|
||||||
, toProxy: true
|
, 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) {
|
return function (req, res, next) {
|
||||||
var hostname = req.headers.host.split(':')[0];
|
var hostname = req.headers.host.split(':')[0];
|
||||||
var relevant = mod.domains.some(function (pattern) {
|
var relevant = mod.domains.some(function (pattern) {
|
||||||
|
@ -51,6 +79,7 @@ module.exports.create = function (deps, conf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
app.use(redirectHttps);
|
app.use(redirectHttps);
|
||||||
|
app.use(handleAdmin);
|
||||||
|
|
||||||
(conf.http.modules || []).forEach(function (mod) {
|
(conf.http.modules || []).forEach(function (mod) {
|
||||||
if (mod.name === 'proxy') {
|
if (mod.name === 'proxy') {
|
||||||
|
|
Loading…
Reference in New Issue