diff --git a/lib/insecure-server.js b/lib/insecure-server.js
index 9be997b..fbda75b 100644
--- a/lib/insecure-server.js
+++ b/lib/insecure-server.js
@@ -1,9 +1,10 @@
'use strict';
-var http = require('http');
-var escapeRe = require('escape-string-regexp');
-
module.exports.create = function (securePort, insecurePort, redirects) {
+ var PromiseA = require('bluebird').Promise;
+ var http = require('http');
+ var escapeRe;
+
function redirectHttps(req, res) {
var insecureRedirects;
var host = req.headers.host || '';
@@ -20,6 +21,11 @@ module.exports.create = function (securePort, insecurePort, redirects) {
return hlen;
}).forEach(function (redirect) {
var origHost = host;
+
+ if (!escapeRe) {
+ escapeRe = require('escape-string-regexp');
+ }
+
// TODO if '*' === hostname[0], omit '^'
host = host.replace(
new RegExp('^' + escapeRe(redirect.from.hostname))
@@ -42,11 +48,11 @@ module.exports.create = function (securePort, insecurePort, redirects) {
+ '\n'
+ '
\n'
+ ' \n'
- + ' \n'
+ + ' \n'
+ '\n'
+ '\n'
+ ' You requested an insecure resource. Please use this instead: \n'
- + ' ' + encodeURI(newLocation) + '
\n'
+ + ' ' + newLocation + '\n'
+ '\n'
+ '\n'
;
@@ -83,7 +89,9 @@ module.exports.create = function (securePort, insecurePort, redirects) {
insecureServer = http.createServer();
insecureServer.on('request', redirectHttps);
insecureServer.listen(insecurePort, function () {
- console.log("\nListening on https://localhost:" + insecureServer.address().port);
- console.log("(redirecting all traffic to https)\n");
+ console.log("\nListening on https://localhost:" + insecureServer.address().port);
+ console.log("(redirecting all traffic to https)\n");
});
+
+ return PromiseA.resolve(insecureServer);
};
diff --git a/lib/vhost-sni-server.js b/lib/vhost-sni-server.js
index 2193940..ee03844 100644
--- a/lib/vhost-sni-server.js
+++ b/lib/vhost-sni-server.js
@@ -1,7 +1,8 @@
'use strict';
-
+
module.exports.create = function (securePort, certsPath, vhostsdir) {
var PromiseA = require('bluebird').Promise;
+ var serveStatic;
var https = require('https');
var fs = require('fs');
var path = require('path');
@@ -28,7 +29,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
return dummyCerts;
}
- function handleAppScopedError(req, res, fn) {
+ function handleAppScopedError(domaininfo, req, res, fn) {
function next(err) {
if (!err) {
fn(req, res);
@@ -69,7 +70,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
// workaround for v0.12 / v1.2 backwards compat
try {
return require('tls').createSecureContext(certs);
- } catch(e) {
+ } catch(e) {
return require('crypto').createCredentials(certs).context;
}
}
@@ -99,6 +100,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
}
function loadDomainMounts(domaininfo) {
+ var connectContext = {};
var appContext;
// 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);
domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname] = function (req, res, next) {
- if (appContext) {
- appContext(req, res, next);
+ function nextify() {
+ 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(
+ ""
+ + ""
+ + ''
+ + ""
+ + ""
+ + "Cannot "
+ + encodeURI(req.method)
+ + " 'https://"
+ + encodeURI(domaininfo.hostname)
+ + '/'
+ + encodeURI(domaininfo.pathname ? (domaininfo.pathname + '/') : '')
+ + encodeURI(req.url.replace(/^\//, ''))
+ + "'"
+ + "
"
+ + "
"
+ + "Domain: " + encodeURI(domaininfo.hostname)
+ + "
"
+ + "App: " + encodeURI(domaininfo.pathname)
+ + "
"
+ + "Route : " + encodeURI(req.url)
+ + ""
+ + ""
+ );
+ /*
+ 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;
}
- 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(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(
- ""
- + ""
- + ''
- + ""
- + ""
- + "Cannot "
- + encodeURI(req.method)
- + " 'https://"
- + encodeURI(domaininfo.hostname)
- + '/'
- + encodeURI(domaininfo.pathname ? (domaininfo.pathname + '/') : '')
- + encodeURI(req.url.replace(/^\//, ''))
- + "'"
- + "
"
- + "
"
- + "Domain: " + encodeURI(domaininfo.hostname)
- + "
"
- + "App: " + encodeURI(domaininfo.pathname)
- + "
"
- + "Route : " + encodeURI(req.url)
- + ""
- + ""
- );
- /*
- 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." } }');
- }
- });
+ connectContext.static(req, res, nextify);
};
domainMergeMap[domaininfo.hostname].apps.use(
'/' + domaininfo.pathname
- , domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]
+ , domainMergeMap[domaininfo.hostname].mountsMap['/' + domaininfo.dirpathname]
);
return PromiseA.resolve();
@@ -302,7 +322,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
}
return forEachAsync(domainMergeMap[vhost].apps, function (fn) {
- return new PromiseA(function (resolve) {
+ return new PromiseA(function (resolve, reject) {
function next(err) {
if (err) {
reject(err);
@@ -341,7 +361,7 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
return;
});
});
- };
+ }
function loadCerts(domainname) {
// 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
var certsPath = path.join(vhostsdir, domainname, 'certs');
+ var secOpts;
try {
var nodes = fs.readdirSync(path.join(certsPath, 'server'));
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 secOpts = {
+ secOpts = {
key: fs.readFileSync(path.join(certsPath, 'server', keyNode))
, cert: fs.readFileSync(path.join(certsPath, 'server', crtNode))
- }
+ };
if (fs.existsSync(path.join(certsPath, 'ca'))) {
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]) {
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
@@ -472,4 +494,4 @@ module.exports.create = function (securePort, certsPath, vhostsdir) {
}
return runServer();
-}
+};