From 38de3e9d1c8372fa6199e64f9edae756f0cd496c Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Fri, 17 Aug 2018 20:43:32 -0600 Subject: [PATCH] v2.4.1: allow two function listeners and return server.unecrypted for error listening --- README.md | 9 +++++++++ index.js | 45 +++++++++++++++++++++++++++++++++------------ package.json | 2 +- test/greenlock.js | 23 ++++++++++++++++++----- 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 9d7e6b4..2522b75 100644 --- a/README.md +++ b/README.md @@ -330,6 +330,15 @@ var server = glx.listen(80, 443, function () { }); ``` +Note: You shouldn't be using the plain HTTP server for anything except, potentially, for error handling +on the listen event (if the default print-and-quit behavior doesn't work for your use case). +If you need to do that, here's how: + +``` +var plainServer = server.unencrypted; +plainServer.on('error', function (err) { ... }); +``` + The Automatic Certificate Issuance is initiated via SNI (`httpsOptions.SNICallback`). For security, domain validation MUST have an approval callback in *production*. diff --git a/index.js b/index.js index 41ece89..9e77029 100644 --- a/index.js +++ b/index.js @@ -29,7 +29,7 @@ module.exports.create = function (opts) { console.error(e.code + ": '" + e.address + ":" + e.port + "'"); } - function _listenHttp(plainPort, sayAnything) { + function _listenHttp(plainPort) { if (!plainPort) { plainPort = 80; } var p = plainPort; var validHttpPort = (parseInt(p, 10) >= 0); @@ -39,10 +39,7 @@ module.exports.create = function (opts) { ); var promise = new PromiseA(function (resolve) { plainServer.listen(p, function () { - if (sayAnything) { - console.info("Success! Bound to port '" + p + "' to handle ACME challenges and redirect to https"); - } - resolve(); + resolve(plainServer); }).on('error', function (e) { if (plainServer.listenerCount('error') < 2) { console.warn("Did not successfully create http server and bind to port '" + p + "':"); @@ -55,18 +52,21 @@ module.exports.create = function (opts) { return promise; } - function _listenHttps(port, sayAnything) { + function _listenHttps(port) { if (!port) { port = 443; } var p = port; var validHttpsPort = (parseInt(p, 10) >= 0); + var httpType; if (!validHttpsPort) { console.warn("'" + p + "' doesn't seem to be a valid port number for https"); } 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 server = https.createServer( greenlock.tlsOptions @@ -79,11 +79,9 @@ module.exports.create = function (opts) { } }) ); + server.type = httpType; var promise = new PromiseA(function (resolve) { server.listen(p, function () { - if (sayAnything) { - console.info("Success! Serving https on port '" + p + "'"); - } resolve(server); }).on('error', function (e) { if (server.listenerCount('error') < 2) { @@ -103,21 +101,44 @@ module.exports.create = function (opts) { res.end("Hello, World!\nWith Love,\nGreenlock for Express.js"); }; - opts.listen = function (plainPort, port, fn) { + opts.listen = function (plainPort, port, fn1, fn2) { var promises = []; var server; + var plainServer; - promises.push(_listenHttp(plainPort, !fn)); + var fn; + var fnPlain; + if (fn2) { + fn = fn2; + fnPlain = fn1; + } else { + fn = fn1; + } + + promises.push(_listenHttp(plainPort, !fnPlain)); promises.push(_listenHttps(port, !fn)); server = promises[1].server; + plainServer = promises[0].server; + PromiseA.all(promises).then(function () { + // Report h2/https status if ('function' === typeof fn) { fn.apply(server); + } else if (server.listenerCount('listening') < 2) { + console.info("Success! Serving " + server.type + " on port '" + server.address().port + "'"); + } + + // Report plain http status + if ('function' === typeof fnPlain) { + fnPlain.apply(plainServer); + } else if (!fn && plainServer.listenerCount('listening') < 2) { + console.info("Success! Bound to port '" + plainServer.address().port + + "' to handle ACME challenges and redirect to " + server.type); } - return server; }); + server.unencrypted = plainServer; return server; }; diff --git a/package.json b/package.json index 7135200..53964dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "greenlock-express", - "version": "2.4.0", + "version": "2.4.1", "description": "Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems.", "main": "index.js", "homepage": "https://git.coolaj86.com/coolaj86/greenlock-express.js", diff --git a/test/greenlock.js b/test/greenlock.js index f552ae3..c8ef4b7 100644 --- a/test/greenlock.js +++ b/test/greenlock.js @@ -16,7 +16,9 @@ var greenlock = Greenlock.create({ var server1 = greenlock.listen(5080, 5443); server1.on('listening', function () { console.log("### THREE 3333 - All is well server1", this.address()); - server1.close(); + setTimeout(function () { + // so that the address() object doesn't disappear + }, 10); }); setTimeout(function () { var server2 = greenlock.listen(6080, 6443, function () { @@ -32,10 +34,21 @@ setTimeout(function () { }); }, 1000); -var server3 = greenlock.listen(7080, 22, function () { - // ignore +var server3 = greenlock.listen(22, 22, function () { + console.error("Error: expected to get an error when launching plain server on port 22"); +}, function () { + console.error("Error: expected to get an error when launching " + server3.type + " server on port 22"); +}); +server3.unencrypted.on('error', function () { + console.log("Success: caught expected (plain) error"); }); server3.on('error', function () { - console.log("Success: caught expected error"); - server3.close(); + console.log("Success: caught expected " + server3.type + " error"); + //server3.close(); +}); + +var server4 = greenlock.listen(7080, 7443, function () { + console.log('Success: server4: plain'); +}, function () { + console.log('Success: server4: ' + server4.type); });