lazier and lazier (plus early static handling)
This commit is contained in:
parent
8827da6478
commit
a21c503b6e
|
@ -1,9 +1,10 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var http = require('http');
|
|
||||||
var escapeRe = require('escape-string-regexp');
|
|
||||||
|
|
||||||
module.exports.create = function (securePort, insecurePort, redirects) {
|
module.exports.create = function (securePort, insecurePort, redirects) {
|
||||||
|
var PromiseA = require('bluebird').Promise;
|
||||||
|
var http = require('http');
|
||||||
|
var escapeRe;
|
||||||
|
|
||||||
function redirectHttps(req, res) {
|
function redirectHttps(req, res) {
|
||||||
var insecureRedirects;
|
var insecureRedirects;
|
||||||
var host = req.headers.host || '';
|
var host = req.headers.host || '';
|
||||||
|
@ -20,6 +21,11 @@ module.exports.create = function (securePort, insecurePort, redirects) {
|
||||||
return hlen;
|
return hlen;
|
||||||
}).forEach(function (redirect) {
|
}).forEach(function (redirect) {
|
||||||
var origHost = host;
|
var origHost = host;
|
||||||
|
|
||||||
|
if (!escapeRe) {
|
||||||
|
escapeRe = require('escape-string-regexp');
|
||||||
|
}
|
||||||
|
|
||||||
// TODO if '*' === hostname[0], omit '^'
|
// TODO if '*' === hostname[0], omit '^'
|
||||||
host = host.replace(
|
host = host.replace(
|
||||||
new RegExp('^' + escapeRe(redirect.from.hostname))
|
new RegExp('^' + escapeRe(redirect.from.hostname))
|
||||||
|
@ -42,11 +48,11 @@ module.exports.create = function (securePort, insecurePort, redirects) {
|
||||||
+ '<html>\n'
|
+ '<html>\n'
|
||||||
+ '<head>\n'
|
+ '<head>\n'
|
||||||
+ ' <style>* { background-color: white; color: white; text-decoration: none; }</style>\n'
|
+ ' <style>* { background-color: white; color: white; text-decoration: none; }</style>\n'
|
||||||
+ ' <META http-equiv="refresh" content="0;URL=' + encodeURI(newLocation) + '">\n'
|
+ ' <META http-equiv="refresh" content="0;URL=' + newLocation + '">\n'
|
||||||
+ '</head>\n'
|
+ '</head>\n'
|
||||||
+ '<body style="display: none;">\n'
|
+ '<body style="display: none;">\n'
|
||||||
+ ' <p>You requested an insecure resource. Please use this instead: \n'
|
+ ' <p>You requested an insecure resource. Please use this instead: \n'
|
||||||
+ ' <a href="' + encodeURI(newLocation) + '">' + encodeURI(newLocation) + '</a></p>\n'
|
+ ' <a href="' + newLocation + '">' + newLocation + '</a></p>\n'
|
||||||
+ '</body>\n'
|
+ '</body>\n'
|
||||||
+ '</html>\n'
|
+ '</html>\n'
|
||||||
;
|
;
|
||||||
|
@ -83,7 +89,9 @@ module.exports.create = function (securePort, insecurePort, redirects) {
|
||||||
insecureServer = http.createServer();
|
insecureServer = http.createServer();
|
||||||
insecureServer.on('request', redirectHttps);
|
insecureServer.on('request', redirectHttps);
|
||||||
insecureServer.listen(insecurePort, function () {
|
insecureServer.listen(insecurePort, function () {
|
||||||
console.log("\nListening on https://localhost:" + insecureServer.address().port);
|
console.log("\nListening on https://localhost:" + insecureServer.address().port);
|
||||||
console.log("(redirecting all traffic to https)\n");
|
console.log("(redirecting all traffic to https)\n");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return PromiseA.resolve(insecureServer);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
module.exports.create = function (securePort, certsPath, vhostsdir) {
|
module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
var PromiseA = require('bluebird').Promise;
|
var PromiseA = require('bluebird').Promise;
|
||||||
|
var serveStatic;
|
||||||
var https = require('https');
|
var https = require('https');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
@ -28,7 +29,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
return dummyCerts;
|
return dummyCerts;
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleAppScopedError(req, res, fn) {
|
function handleAppScopedError(domaininfo, req, res, fn) {
|
||||||
function next(err) {
|
function next(err) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
fn(req, res);
|
fn(req, res);
|
||||||
|
@ -69,7 +70,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
// workaround for v0.12 / v1.2 backwards compat
|
// workaround for v0.12 / v1.2 backwards compat
|
||||||
try {
|
try {
|
||||||
return require('tls').createSecureContext(certs);
|
return require('tls').createSecureContext(certs);
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
return require('crypto').createCredentials(certs).context;
|
return require('crypto').createCredentials(certs).context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +100,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadDomainMounts(domaininfo) {
|
function loadDomainMounts(domaininfo) {
|
||||||
|
var connectContext = {};
|
||||||
var appContext;
|
var appContext;
|
||||||
|
|
||||||
// should order and group by longest domain, then longest path
|
// should order and group by longest domain, then longest path
|
||||||
|
@ -119,85 +121,103 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
|
|
||||||
console.log('[log] [once] Preparing mount for', domaininfo.hostname + '/' + domaininfo.dirpathname);
|
console.log('[log] [once] Preparing mount for', domaininfo.hostname + '/' + domaininfo.dirpathname);
|
||||||
domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname] = function (req, res, next) {
|
domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname] = function (req, res, next) {
|
||||||
if (appContext) {
|
function nextify() {
|
||||||
appContext(req, res, next);
|
if (appContext) {
|
||||||
|
appContext(req, res, next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[log] LOADING "' + domaininfo.hostname + '/' + domaininfo.pathname + '"', req.url);
|
||||||
|
getAppContext(domaininfo).then(function (localApp) {
|
||||||
|
//if (localApp.arity >= 2) { /* connect uses .apply(null, arguments)*/ }
|
||||||
|
if ('function' !== typeof localApp) {
|
||||||
|
localApp = getDummyAppContext(null, "[ERROR] no connect-style export from " + domaininfo.dirname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: pathname should NEVER have a leading '/' on its own
|
||||||
|
// we always add it explicitly
|
||||||
|
function localAppWrapped(req, res) {
|
||||||
|
console.log('[debug]', domaininfo.hostname + '/' + domaininfo.pathname, req.url);
|
||||||
|
localApp(req, res, handleAppScopedError(domaininfo, req, res, function (req, res) {
|
||||||
|
if (!serveFavicon) {
|
||||||
|
serveFavicon = require('serve-favicon')(path.join(__dirname, '..', 'public', 'favicon.ico'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO redirect GET /favicon.ico to GET (req.headers.referer||'') + /favicon.ico
|
||||||
|
// TODO other common root things - robots.txt, app-icon, etc
|
||||||
|
serveFavicon(req, res, handleAppScopedError(domaininfo, req, res, function (req, res) {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end(
|
||||||
|
"<html>"
|
||||||
|
+ "<head>"
|
||||||
|
+ '<link rel="icon" href="favicon.ico" />'
|
||||||
|
+ "</head>"
|
||||||
|
+ "<body>"
|
||||||
|
+ "Cannot "
|
||||||
|
+ encodeURI(req.method)
|
||||||
|
+ " 'https://"
|
||||||
|
+ encodeURI(domaininfo.hostname)
|
||||||
|
+ '/'
|
||||||
|
+ encodeURI(domaininfo.pathname ? (domaininfo.pathname + '/') : '')
|
||||||
|
+ encodeURI(req.url.replace(/^\//, ''))
|
||||||
|
+ "'"
|
||||||
|
+ "<br/>"
|
||||||
|
+ "<br/>"
|
||||||
|
+ "Domain: " + encodeURI(domaininfo.hostname)
|
||||||
|
+ "<br/>"
|
||||||
|
+ "App: " + encodeURI(domaininfo.pathname)
|
||||||
|
+ "<br/>"
|
||||||
|
+ "Route : " + encodeURI(req.url)
|
||||||
|
+ "</body>"
|
||||||
|
+ "</html>"
|
||||||
|
);
|
||||||
|
/*
|
||||||
|
res.end('{ "error": { "messages": "Route matched '
|
||||||
|
+ domaininfo.hostname + '/' + domaininfo.pathname
|
||||||
|
+ ', but was not handled. Forcing hard stop to prevent fallthru." } }');
|
||||||
|
*/
|
||||||
|
}));
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
domainMergeMap[domaininfo.hostname].apps.use('/' + domaininfo.pathname, localAppWrapped);
|
||||||
|
console.info('Loaded ' + domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname);
|
||||||
|
appContext = localAppWrapped;
|
||||||
|
appContext(req, res, next);
|
||||||
|
} catch(e) {
|
||||||
|
console.error('[ERROR] '
|
||||||
|
+ domaininfo.hostname + ':' + securePort
|
||||||
|
+ '/' + domaininfo.pathname
|
||||||
|
);
|
||||||
|
console.error(e);
|
||||||
|
// TODO this may not work in web apps (due to 500), probably okay
|
||||||
|
res.writeHead(500);
|
||||||
|
res.end('{ "error": { "message": "[ERROR] could not load '
|
||||||
|
+ encodeURI(domaininfo.hostname) + ':' + securePort + '/' + encodeURI(domaininfo.pathname)
|
||||||
|
+ 'or default error app." } }');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!serveStatic) {
|
||||||
|
serveStatic = require('serve-static');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connectContext.static) {
|
||||||
|
console.log('[static]', path.join(vhostsdir, domaininfo.dirname, 'public'));
|
||||||
|
connectContext.static = serveStatic(path.join(vhostsdir, domaininfo.dirname, 'public'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^\/api\//.test(req.url)) {
|
||||||
|
nextify();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[log] LOADING "' + domaininfo.hostname + '/' + domaininfo.pathname + '"', req.url);
|
connectContext.static(req, res, nextify);
|
||||||
getAppContext(domaininfo).then(function (localApp) {
|
|
||||||
//if (localApp.arity >= 2) { /* connect uses .apply(null, arguments)*/ }
|
|
||||||
if ('function' !== typeof localApp) {
|
|
||||||
localApp = getDummyAppContext(null, "[ERROR] no connect-style export from " + domaininfo.dirname);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: pathname should NEVER have a leading '/' on its own
|
|
||||||
// we always add it explicitly
|
|
||||||
function localAppWrapped(req, res) {
|
|
||||||
console.log('[debug]', domaininfo.hostname + '/' + domaininfo.pathname, req.url);
|
|
||||||
localApp(req, res, handleAppScopedError(req, res, function (req, res) {
|
|
||||||
if (!serveFavicon) {
|
|
||||||
serveFavicon = require('serve-favicon')(path.join(__dirname, '..', 'public', 'favicon.ico'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO redirect GET /favicon.ico to GET (req.headers.referer||'') + /favicon.ico
|
|
||||||
// TODO other common root things - robots.txt, app-icon, etc
|
|
||||||
serveFavicon(req, res, handleAppScopedError(req, res, function (req, res) {
|
|
||||||
res.writeHead(404);
|
|
||||||
res.end(
|
|
||||||
"<html>"
|
|
||||||
+ "<head>"
|
|
||||||
+ '<link rel="icon" href="favicon.ico" />'
|
|
||||||
+ "</head>"
|
|
||||||
+ "<body>"
|
|
||||||
+ "Cannot "
|
|
||||||
+ encodeURI(req.method)
|
|
||||||
+ " 'https://"
|
|
||||||
+ encodeURI(domaininfo.hostname)
|
|
||||||
+ '/'
|
|
||||||
+ encodeURI(domaininfo.pathname ? (domaininfo.pathname + '/') : '')
|
|
||||||
+ encodeURI(req.url.replace(/^\//, ''))
|
|
||||||
+ "'"
|
|
||||||
+ "<br/>"
|
|
||||||
+ "<br/>"
|
|
||||||
+ "Domain: " + encodeURI(domaininfo.hostname)
|
|
||||||
+ "<br/>"
|
|
||||||
+ "App: " + encodeURI(domaininfo.pathname)
|
|
||||||
+ "<br/>"
|
|
||||||
+ "Route : " + encodeURI(req.url)
|
|
||||||
+ "</body>"
|
|
||||||
+ "</html>"
|
|
||||||
);
|
|
||||||
/*
|
|
||||||
res.end('{ "error": { "messages": "Route matched '
|
|
||||||
+ domaininfo.hostname + '/' + domaininfo.pathname
|
|
||||||
+ ', but was not handled. Forcing hard stop to prevent fallthru." } }');
|
|
||||||
*/
|
|
||||||
}));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
domainMergeMap[domaininfo.hostname].apps.use('/' + domaininfo.pathname, localAppWrapped);
|
|
||||||
console.info('Loaded ' + domaininfo.hostname + ':' + securePort + '/' + domaininfo.pathname);
|
|
||||||
appContext = localAppWrapped;
|
|
||||||
appContext(req, res, next);
|
|
||||||
} catch(e) {
|
|
||||||
console.error('[ERROR] '
|
|
||||||
+ domaininfo.hostname + ':' + securePort
|
|
||||||
+ '/' + domaininfo.pathname
|
|
||||||
);
|
|
||||||
console.error(e);
|
|
||||||
// TODO this may not work in web apps (due to 500), probably okay
|
|
||||||
res.writeHead(500);
|
|
||||||
res.end('{ "error": { "message": "[ERROR] could not load '
|
|
||||||
+ encodeURI(domaininfo.hostname) + ':' + securePort + '/' + encodeURI(domaininfo.pathname)
|
|
||||||
+ 'or default error app." } }');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
domainMergeMap[domaininfo.hostname].apps.use(
|
domainMergeMap[domaininfo.hostname].apps.use(
|
||||||
'/' + domaininfo.pathname
|
'/' + domaininfo.pathname
|
||||||
, domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]
|
, domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]
|
||||||
);
|
);
|
||||||
|
|
||||||
return PromiseA.resolve();
|
return PromiseA.resolve();
|
||||||
|
@ -302,7 +322,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return forEachAsync(domainMergeMap[vhost].apps, function (fn) {
|
return forEachAsync(domainMergeMap[vhost].apps, function (fn) {
|
||||||
return new PromiseA(function (resolve) {
|
return new PromiseA(function (resolve, reject) {
|
||||||
function next(err) {
|
function next(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
|
@ -341,7 +361,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function loadCerts(domainname) {
|
function loadCerts(domainname) {
|
||||||
// TODO make async
|
// TODO make async
|
||||||
|
@ -349,15 +369,16 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
// Also, once we load Let's Encrypt, it's lights out for v0.10
|
// Also, once we load Let's Encrypt, it's lights out for v0.10
|
||||||
|
|
||||||
var certsPath = path.join(vhostsdir, domainname, 'certs');
|
var certsPath = path.join(vhostsdir, domainname, 'certs');
|
||||||
|
var secOpts;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var nodes = fs.readdirSync(path.join(certsPath, 'server'));
|
var nodes = fs.readdirSync(path.join(certsPath, 'server'));
|
||||||
var keyNode = nodes.filter(function (node) { return /\.key\.pem$/.test(node); })[0];
|
var keyNode = nodes.filter(function (node) { return /\.key\.pem$/.test(node); })[0];
|
||||||
var crtNode = nodes.filter(function (node) { return /\.crt\.pem$/.test(node); })[0];
|
var crtNode = nodes.filter(function (node) { return /\.crt\.pem$/.test(node); })[0];
|
||||||
var secOpts = {
|
secOpts = {
|
||||||
key: fs.readFileSync(path.join(certsPath, 'server', keyNode))
|
key: fs.readFileSync(path.join(certsPath, 'server', keyNode))
|
||||||
, cert: fs.readFileSync(path.join(certsPath, 'server', crtNode))
|
, cert: fs.readFileSync(path.join(certsPath, 'server', crtNode))
|
||||||
}
|
};
|
||||||
|
|
||||||
if (fs.existsSync(path.join(certsPath, 'ca'))) {
|
if (fs.existsSync(path.join(certsPath, 'ca'))) {
|
||||||
secOpts.ca = fs.readdirSync(path.join(certsPath, 'ca')).filter(function (node) {
|
secOpts.ca = fs.readdirSync(path.join(certsPath, 'ca')).filter(function (node) {
|
||||||
|
@ -419,7 +440,8 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
|
|
||||||
if (!secureContexts[domainname]) {
|
if (!secureContexts[domainname]) {
|
||||||
console.log('[log] Loading certs for', domainname);
|
console.log('[log] Loading certs for', domainname);
|
||||||
secureContexts[domainname] = loadCerts(domainname);
|
// TODO keep trying to find the cert in case it's uploaded late?
|
||||||
|
secureContexts[domainname] = loadCerts(domainname) || secureContexts.dummy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround for v0.12 / v1.2 backwards compat bug
|
// workaround for v0.12 / v1.2 backwards compat bug
|
||||||
|
@ -472,4 +494,4 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return runServer();
|
return runServer();
|
||||||
}
|
};
|
||||||
|
|
Loading…
Reference in New Issue