|
|
@ -30,7 +30,7 @@ module.exports.create = function (opts) { |
|
|
|
console.error(e.code + ": '" + e.address + ":" + e.port + "'"); |
|
|
|
} |
|
|
|
|
|
|
|
function _listen(plainPort, plain) { |
|
|
|
function _createPlain(plainPort) { |
|
|
|
if (!plainPort) { plainPort = 80; } |
|
|
|
|
|
|
|
var parts = String(plainPort).split(':'); |
|
|
@ -41,53 +41,137 @@ module.exports.create = function (opts) { |
|
|
|
var server; |
|
|
|
var validHttpPort = (parseInt(p, 10) >= 0); |
|
|
|
|
|
|
|
function tryPlain() { |
|
|
|
server = require('http').createServer( |
|
|
|
greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')())) |
|
|
|
); |
|
|
|
httpType = 'http'; |
|
|
|
if (addr) { args[1] = addr; } |
|
|
|
if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) { |
|
|
|
console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe"); |
|
|
|
} |
|
|
|
|
|
|
|
function trySecure() { |
|
|
|
var https; |
|
|
|
try { |
|
|
|
https = require('spdy'); |
|
|
|
greenlock.tlsOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false }; |
|
|
|
httpType = 'http2 (spdy/h2)'; |
|
|
|
} catch(e) { |
|
|
|
https = require('https'); |
|
|
|
httpType = 'https'; |
|
|
|
} |
|
|
|
server = https.createServer( |
|
|
|
greenlock.tlsOptions |
|
|
|
, greenlock.middleware.sanitizeHost(function (req, res) { |
|
|
|
try { |
|
|
|
greenlock.app(req, res); |
|
|
|
} catch(e) { |
|
|
|
console.error("[error] [greenlock.app] Your HTTP handler had an uncaught error:"); |
|
|
|
console.error(e); |
|
|
|
try { |
|
|
|
res.statusCode = 500; |
|
|
|
res.end("Internal Server Error: [Greenlock] HTTP exception logged for user-provided handler."); |
|
|
|
} catch(e) { |
|
|
|
// ignore
|
|
|
|
// (headers may have already been sent, etc)
|
|
|
|
} |
|
|
|
server = require('http').createServer( |
|
|
|
greenlock.middleware.sanitizeHost(greenlock.middleware(require('redirect-https')())) |
|
|
|
); |
|
|
|
httpType = 'http'; |
|
|
|
|
|
|
|
return { server: server, listen: function () { return new Promise(function (resolve, reject) { |
|
|
|
args[0] = p; |
|
|
|
args.push(function () { |
|
|
|
if (!greenlock.servername) { |
|
|
|
if (Array.isArray(greenlock.approvedDomains) && greenlock.approvedDomains.length) { |
|
|
|
greenlock.servername = greenlock.approvedDomains[0]; |
|
|
|
} |
|
|
|
}) |
|
|
|
); |
|
|
|
server.type = httpType; |
|
|
|
} |
|
|
|
if (Array.isArray(greenlock.approveDomains) && greenlock.approvedDomains.length) { |
|
|
|
greenlock.servername = greenlock.approvedDomains[0]; |
|
|
|
} |
|
|
|
} |
|
|
|
if (!greenlock.servername) { |
|
|
|
resolve(null); |
|
|
|
return; |
|
|
|
} |
|
|
|
return greenlock.check({ domains: [ greenlock.servername ] }).then(function (certs) { |
|
|
|
if (certs) { |
|
|
|
console.info("Using '%s' as default certificate", greenlock.servername); |
|
|
|
return { |
|
|
|
key: Buffer.from(certs.privkey, 'ascii') |
|
|
|
, cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii') |
|
|
|
}; |
|
|
|
} |
|
|
|
console.info("Fetching certificate for '%s' to use as default for HTTPS server...", greenlock.servername); |
|
|
|
return new PromiseA(function (resolve, reject) { |
|
|
|
// using SNICallback because all options will be set
|
|
|
|
greenlock.tlsOptions.SNICallback(greenlock.servername, function (err/*, secureContext*/) { |
|
|
|
if (err) { reject(err); return; } |
|
|
|
return greenlock.check({ domains: [ greenlock.servername ] }).then(function (certs) { |
|
|
|
resolve({ |
|
|
|
key: Buffer.from(certs.privkey, 'ascii') |
|
|
|
, cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii') |
|
|
|
}); |
|
|
|
}).catch(reject); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}).then(resolve).catch(reject); |
|
|
|
}); |
|
|
|
server.listen.apply(server, args).on('error', function (e) { |
|
|
|
if (server.listenerCount('error') < 2) { |
|
|
|
console.warn("Did not successfully create http server and bind to port '" + p + "':"); |
|
|
|
explainError(e); |
|
|
|
process.exit(41); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); } }; |
|
|
|
} |
|
|
|
|
|
|
|
function _create(port) { |
|
|
|
if (!port) { port = 443; } |
|
|
|
|
|
|
|
var parts = String(port).split(':'); |
|
|
|
var p = parts.pop(); |
|
|
|
var addr = parts.join(':').replace(/^\[/, '').replace(/\]$/, ''); |
|
|
|
var args = []; |
|
|
|
var httpType; |
|
|
|
var server; |
|
|
|
var validHttpPort = (parseInt(p, 10) >= 0); |
|
|
|
|
|
|
|
if (addr) { args[1] = addr; } |
|
|
|
if (!validHttpPort && !/(\/)|(\\\\)/.test(p)) { |
|
|
|
console.warn("'" + p + "' doesn't seem to be a valid port number, socket path, or pipe"); |
|
|
|
} |
|
|
|
if (plain) { tryPlain(); } else { trySecure(); } |
|
|
|
|
|
|
|
var promise = new PromiseA(function (resolve) { |
|
|
|
var https; |
|
|
|
try { |
|
|
|
https = require('spdy'); |
|
|
|
greenlock.tlsOptions.spdy = { protocols: [ 'h2', 'http/1.1' ], plain: false }; |
|
|
|
httpType = 'http2 (spdy/h2)'; |
|
|
|
} catch(e) { |
|
|
|
https = require('https'); |
|
|
|
httpType = 'https'; |
|
|
|
} |
|
|
|
var sniCallback = greenlock.tlsOptions.SNICallback; |
|
|
|
greenlock.tlsOptions.SNICallback = function (domain, cb) { |
|
|
|
sniCallback(domain, function (err, context) { |
|
|
|
cb(err, context); |
|
|
|
if (!context || server._hasDefaultSecureContext) { |
|
|
|
return; |
|
|
|
} |
|
|
|
return greenlock.check({ domains: [ domain ] }).then(function (certs) { |
|
|
|
// ignore the case that check doesn't have all the right args here
|
|
|
|
// to get the same certs that it just got (eventually the right ones will come in)
|
|
|
|
if (!certs) { return; } |
|
|
|
console.info("Using '%s' as default certificate", domain); |
|
|
|
server.setSecureContext({ |
|
|
|
key: Buffer.from(certs.privkey, 'ascii') |
|
|
|
, cert: Buffer.from(certs.cert + '\r\n' + certs.chain, 'ascii') |
|
|
|
}); |
|
|
|
server._hasDefaultSecureContext = true; |
|
|
|
}).catch(function (/*e*/) { |
|
|
|
// this may be that the test.example.com was requested, but it's listed
|
|
|
|
// on the cert for demo.example.com which is in its own directory, not the other
|
|
|
|
//console.warn("Unusual error: couldn't get newly authorized certificate:");
|
|
|
|
//console.warn(e.message);
|
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
|
server = https.createServer( |
|
|
|
greenlock.tlsOptions |
|
|
|
, greenlock.middleware.sanitizeHost(function (req, res) { |
|
|
|
try { |
|
|
|
greenlock.app(req, res); |
|
|
|
} catch(e) { |
|
|
|
console.error("[error] [greenlock.app] Your HTTP handler had an uncaught error:"); |
|
|
|
console.error(e); |
|
|
|
try { |
|
|
|
res.statusCode = 500; |
|
|
|
res.end("Internal Server Error: [Greenlock] HTTP exception logged for user-provided handler."); |
|
|
|
} catch(e) { |
|
|
|
// ignore
|
|
|
|
// (headers may have already been sent, etc)
|
|
|
|
} |
|
|
|
} |
|
|
|
}) |
|
|
|
); |
|
|
|
server.type = httpType; |
|
|
|
|
|
|
|
return { server: server, listen: function () { return new PromiseA(function (resolve) { |
|
|
|
args[0] = p; |
|
|
|
args.push(function () { resolve(server); }); |
|
|
|
args.push(function () { resolve(/*server*/); }); |
|
|
|
server.listen.apply(server, args).on('error', function (e) { |
|
|
|
if (server.listenerCount('error') < 2) { |
|
|
|
console.warn("Did not successfully create http server and bind to port '" + p + "':"); |
|
|
@ -95,10 +179,7 @@ module.exports.create = function (opts) { |
|
|
|
process.exit(41); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
promise.server = server; |
|
|
|
return promise; |
|
|
|
}); } }; |
|
|
|
} |
|
|
|
|
|
|
|
// NOTE: 'greenlock' is just 'opts' renamed
|
|
|
@ -111,7 +192,6 @@ module.exports.create = function (opts) { |
|
|
|
} |
|
|
|
|
|
|
|
opts.listen = function (plainPort, port, fnPlain, fn) { |
|
|
|
var promises = []; |
|
|
|
var server; |
|
|
|
var plainServer; |
|
|
|
|
|
|
@ -122,28 +202,34 @@ module.exports.create = function (opts) { |
|
|
|
fnPlain = null; |
|
|
|
} |
|
|
|
|
|
|
|
promises.push(_listen(plainPort, true)); |
|
|
|
promises.push(_listen(port, false)); |
|
|
|
var obj1 = _createPlain(plainPort, true); |
|
|
|
var obj2 = _create(port, false); |
|
|
|
|
|
|
|
server = promises[1].server; |
|
|
|
plainServer = promises[0].server; |
|
|
|
plainServer = obj1.server; |
|
|
|
server = obj2.server; |
|
|
|
|
|
|
|
PromiseA.all(promises).then(function () { |
|
|
|
// Report plain http status
|
|
|
|
if ('function' === typeof fnPlain) { |
|
|
|
fnPlain.apply(plainServer); |
|
|
|
} else if (!fn && !plainServer.listenerCount('listening') && !server.listenerCount('listening')) { |
|
|
|
console.info('[:' + (plainServer.address().port || plainServer.address()) |
|
|
|
+ "] Handling ACME challenges and redirecting to " + server.type); |
|
|
|
server.then = obj1.listen().then(function (tlsOptions) { |
|
|
|
if (tlsOptions) { |
|
|
|
server.setSecureContext(tlsOptions); |
|
|
|
server._hasDefaultSecureContext = true; |
|
|
|
} |
|
|
|
return obj2.listen().then(function () { |
|
|
|
// Report plain http status
|
|
|
|
if ('function' === typeof fnPlain) { |
|
|
|
fnPlain.apply(plainServer); |
|
|
|
} else if (!fn && !plainServer.listenerCount('listening') && !server.listenerCount('listening')) { |
|
|
|
console.info('[:' + (plainServer.address().port || plainServer.address()) |
|
|
|
+ "] Handling ACME challenges and redirecting to " + server.type); |
|
|
|
} |
|
|
|
|
|
|
|
// Report h2/https status
|
|
|
|
if ('function' === typeof fn) { |
|
|
|
fn.apply(server); |
|
|
|
} else if (!server.listenerCount('listening')) { |
|
|
|
console.info('[:' + (server.address().port || server.address()) + "] Serving " + server.type); |
|
|
|
} |
|
|
|
}); |
|
|
|
// Report h2/https status
|
|
|
|
if ('function' === typeof fn) { |
|
|
|
fn.apply(server); |
|
|
|
} else if (!server.listenerCount('listening')) { |
|
|
|
console.info('[:' + (server.address().port || server.address()) + "] Serving " + server.type); |
|
|
|
} |
|
|
|
}); |
|
|
|
}).then; |
|
|
|
|
|
|
|
server.unencrypted = plainServer; |
|
|
|
return server; |
|
|
|