move towards full hotloading

This commit is contained in:
AJ ONeal 2015-02-19 22:08:33 +00:00
parent d5a7ab6049
commit be2c73ef08
2 changed files with 132 additions and 95 deletions

View File

@ -10,21 +10,7 @@ var connect = require('connect');
var vhost = require('vhost');
module.exports.create = function (securePort, certsPath, vhostsdir) {
// connect / express app
var app = connect();
// SSL Server
var secureContexts = {};
var dummyCerts;
var secureOpts;
var secureServer;
// the ssl domains I have
// TODO read vhosts minus
var domains = fs.readdirSync(vhostsdir).filter(function (node) {
// not a hidden or private file
return '.' !== node[0] && '_' !== node[0];
}).map(function (apppath) {
function getDomainInfo(apppath) {
var parts = apppath.split(/[#%]+/);
var hostname = parts.shift();
var pathname = parts.join('/').replace(/\/+/g, '/').replace(/^\//, '');
@ -32,10 +18,76 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
return {
hostname: hostname
, pathname: pathname
, dirpathname: parts.join('#')
, dirname: apppath
, isRoot: apppath === hostname
};
}).sort(function (a, b) {
}
function loadDomainMounts(domaininfo) {
var appContext;
// should order and group by longest domain, then longest path
if (!domainMergeMap[domaininfo.hostname]) {
// create an connect / express app exclusive to this domain
// TODO express??
domainMergeMap[domaininfo.hostname] = {
hostname: domaininfo.hostname
, apps: connect()
, mountsMap: {}
};
domainMerged.push(domainMergeMap[domaininfo.hostname]);
}
if (domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]) {
return;
}
domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname] = function (req, res, next) {
if (appContext) {
appContext(req, res, next);
return;
}
console.log('[log] LOADING "' + domaininfo.hostname + '/' + domaininfo.pathname + '"');
getAppContext(domaininfo).then(function (localApp) {
// Note: pathname should NEVER have a leading '/' on its own
// we always add it explicitly
try {
domainMergeMap[domaininfo.hostname].apps.use('/' + domaininfo.pathname, localApp);
console.info('Loaded ' + domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname);
appContext = localApp;
appContext(req, res, next);
} catch(e) {
console.error('[ERROR] ' + domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname);
console.error(e);
res.send('{ "error": { "message": "[ERROR] could not load '
+ domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname
+ 'or default error app." } }');
}
});
};
domainMergeMap[domaininfo.hostname].apps.use(
'/' + domaininfo.pathname
, domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]
);
return PromiseA.resolve();
}
function loadDomainVhosts() {
domainMerged.forEach(function (domainApp) {
console.log('[log] merged ' + domainApp.hostname);
app.use(vhost(domainApp.hostname, domainApp.apps));
app.use(vhost('www.' + domainApp.hostname, domainApp.apps));
});
}
function readNewVhosts() {
return fs.readdirSync(vhostsdir).filter(function (node) {
// not a hidden or private file
return '.' !== node[0] && '_' !== node[0];
}).map(getDomainInfo).sort(function (a, b) {
var hlen = b.hostname.length - a.hostname.length;
var plen = b.pathname.length - a.pathname.length;
@ -50,9 +102,22 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
}
return plen;
});
}
// connect / express app
var app = connect();
// SSL Server
var secureContexts = {};
var dummyCerts;
var secureOpts;
var secureServer;
/*
var rootDomains = domains.filter(function (domaininfo) {
return domaininfo.isRoot;
});
*/
var domainMergeMap = {};
var domainMerged = [];
@ -66,6 +131,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
}
function getDummyAppContext(err, msg) {
console.error('[ERROR] getDummyAppContext');
console.error(err);
console.error(msg);
return function (req, res) {
@ -163,62 +229,30 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
return secureContexts[domainname];
}
app.use(function (req, res, next) {
console.log('[log] request for ' + req.headers.host + req.url);
next();
});
// TODO load these once the server has started
// TODO pre-cache these once the server has started?
// return forEachAsync(rootDomains, loadCerts);
return forEachAsync(domains, function (domaininfo) {
var appContext;
// TODO load these even more lazily
return forEachAsync(readNewVhosts(), loadDomainMounts).then(loadDomainVhosts).then(runServer);
// should order and group by longest domain, then longest path
function hotloadApp(req, res, next) {
var vhost = (req.headers.host || '').split(':')[0];
if (!domainMergeMap[vhost]) {
// TODO reread directories
}
/*
// TODO loop through mounts and see if any fit
domainMergeMap[vhost].mountsMap['/' + domaininfo.dirpathname]
if (!domainMergeMap[domaininfo.hostname]) {
// create an connect / express app exclusive to this domain
// TODO express??
domainMergeMap[domaininfo.hostname] = { hostname: domaininfo.hostname, apps: connect() };
domainMerged.push(domainMergeMap[domaininfo.hostname]);
// TODO reread directories
}
*/
// TODO hot load all-the-things
next();
}
domainMergeMap[domaininfo.hostname].apps.use(
'/' + domaininfo.pathname
, function (req, res, next) {
if (appContext) {
console.log('[log] has appContext');
appContext(req, res, next);
return;
}
console.log('[log] no appContext');
getAppContext(domaininfo).then(function (localApp) {
// Note: pathname should NEVER have a leading '/' on its own
// we always add it explicitly
try {
domainMergeMap[domaininfo.hostname].apps.use('/' + domaininfo.pathname, localApp);
console.info('Loaded ' + domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname);
appContext = localApp;
appContext(req, res, next);
} catch(e) {
console.error('[ERROR] ' + domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname);
console.error(e);
res.send('{ "error": { "message": "[ERROR] could not load '
+ domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname
+ 'or default error app." } }');
}
});
}
);
return PromiseA.resolve();
}).then(function () {
domainMerged.forEach(function (domainApp) {
console.log('[log] merged ' + domainApp.hostname);
app.use(vhost(domainApp.hostname, domainApp.apps));
app.use(vhost('www.' + domainApp.hostname, domainApp.apps));
});
}).then(runServer);
app.use(hotloadApp);
function runServer() {
//provide a SNICallback when you create the options for the https server
@ -268,7 +302,6 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
secureServer = https.createServer(secureOpts);
secureServer.on('request', function (req, res) {
console.log('[log] request');
app(req, res);
});
secureServer.listen(securePort, function () {

View File

@ -1,4 +1,5 @@
//var holepunch = require('./holepunch/beacon');
'use strict';
//var config = require('./device.json');
var securePort = process.argv[2] || 443;
var insecurePort = process.argv[3] || 80;
@ -10,32 +11,33 @@ var certsPath = path.join(__dirname, 'certs');
// require('ssl-root-cas').inject();
var vhostsdir = path.join(__dirname, 'vhosts');
require('./lib/insecure-server').create(securePort, insecurePort, redirects);
require('./lib/vhost-sni-server.js').create(securePort, certsPath, vhostsdir).then(function () {
function phoneHome() {
var holepunch = require('./holepunch/beacon');
var ports;
ports = [
{ private: 22
, public: 22
{ private: 65022
, public: 65022
, protocol: 'tcp'
, ttl: 0
, test: { service: 'ssh' }
, testable: false
}
, { private: 443
, public: 443
, { private: 650443
, public: 650443
, protocol: 'tcp'
, ttl: 0
, test: { service: 'https' }
}
, { private: 80
, public: 80
, { private: 65080
, public: 65080
, protocol: 'tcp'
, ttl: 0
, test: { service: 'http' }
}
];
//
/*
// TODO return a middleware
holepunch.run(require('./redirects.json').reduce(function (all, redirect) {
@ -52,5 +54,7 @@ require('./lib/vhost-sni-server.js').create(securePort, certsPath, vhostsdir).th
}, []), ports).catch(function () {
console.error("Couldn't phone home. Oh well");
});
*/
});
//*/
}
require('./lib/insecure-server').create(securePort, insecurePort, redirects);
require('./lib/vhost-sni-server.js').create(securePort, certsPath, vhostsdir).then(phoneHome);