diff --git a/http-middleware.js b/http-middleware.js index 149081b..8488ffb 100644 --- a/http-middleware.js +++ b/http-middleware.js @@ -16,41 +16,85 @@ HttpMiddleware.create = function(gl, defaultApp) { explainError(gl, err, "http_01_middleware_socket", hostname); }); - if (skipIfNeedBe(req, res, next, defaultApp, hostname)) { + // Skip unless the path begins with /.well-known/acme-challenge/ + if (!hostname || 0 !== req.url.indexOf(challengePrefix)) { + skipChallenge(req, res, next, defaultApp); return; } + // HEADERS SENT DEBUG NOTE #2 + // at this point, it's most likely Let's Encrypt server + // (or greenlock itself) performing the verification process + // Hmmm... perhaps we should change the greenlock prefix to test + // Anyway, we just got fast the first place where we could + // be sending headers. + var token = req.url.slice(challengePrefix.length); + var done = false; + var countA = 0; + var countB = 0; gl.getAcmeHttp01ChallengeResponse({ type: "http-01", servername: hostname, token: token }) .catch(function(err) { + countA += 1; + // HEADERS SENT DEBUG NOTE #3 + // This is the second possible time we could be sending headers respondToError(gl, res, err, "http_01_middleware_challenge_response", hostname); + done = true; return { __done: true }; }) .then(function(result) { + countB += 1; if (result && result.__done) { return; } + if (done) { + console.error("Sanity check fail: `done` is in a quantum state of both true and false... huh?"); + return; + } + // HEADERS SENT DEBUG NOTE #4b + // This is the third/fourth possible time send headers return respondWithGrace(res, result, hostname, token); + }) + .catch(function(err) { + // HEADERS SENT DEBUG NOTE #5 + // I really don't see how this can be possible. + // Every case appears to be accounted for + console.error(); + console.error("[warning] Developer Error:" + (err.code || err.context || ""), countA, countB); + console.error(err.stack); + console.error(); + console.error( + "This is probably the error that happens routinely on http2 connections, but we're not sure why." + ); + console.error("To track the status or help contribute,"); + console.error("visit: https://git.rootprojects.org/root/greenlock-express.js/issues/9"); + console.error(); + try { + res.end("Internal Server Error [1003]: See logs for details."); + } catch (e) { + // ignore + } }); }; }; -function skipIfNeedBe(req, res, next, defaultApp, hostname) { - if (!hostname || 0 !== req.url.indexOf(challengePrefix)) { - if ("function" === typeof defaultApp) { - defaultApp(req, res, next); - } else if ("function" === typeof next) { - next(); - } else { - res.statusCode = 500; - res.end("[500] Developer Error: app.use('/', greenlock.httpMiddleware()) or greenlock.httpMiddleware(app)"); - } +function skipChallenge(req, res, next, defaultApp) { + if ("function" === typeof defaultApp) { + defaultApp(req, res, next); + } else if ("function" === typeof next) { + next(); + } else { + res.statusCode = 500; + res.end("[500] Developer Error: app.use('/', greenlock.httpMiddleware()) or greenlock.httpMiddleware(app)"); } } function respondWithGrace(res, result, hostname, token) { var keyAuth = result && result.keyAuthorization; + + // HEADERS SENT DEBUG NOTE #4b + // This is (still) the third/fourth possible time we could be sending headers if (keyAuth && "string" === typeof keyAuth) { res.setHeader("Content-Type", "text/plain; charset=utf-8"); res.end(keyAuth); @@ -69,14 +113,18 @@ function explainError(gl, err, ctx, hostname) { if (!err.context) { err.context = ctx; } + // leaving this in the build for now because it will help with existing error reports + console.error("[warning] network connection error:", (err.context || "") + " " + err.message); (gl.notify || gl._notify)("error", err); return err; } function respondToError(gl, res, err, ctx, hostname) { + // HEADERS SENT DEBUG NOTE #3b + // This is (still) the second possible time we could be sending headers err = explainError(gl, err, ctx, hostname); res.statusCode = 500; - res.end("Internal Server Error: See logs for details."); + res.end("Internal Server Error [1004]: See logs for details."); } HttpMiddleware.getHostname = function(req) { diff --git a/package.json b/package.json index a87adc6..751e35d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@root/greenlock-express", - "version": "3.0.15", + "version": "3.0.16", "description": "Free SSL and managed or automatic HTTPS for node.js with Express, Koa, Connect, Hapi, and all other middleware systems.", "main": "greenlock-express.js", "homepage": "https://greenlock.domains", diff --git a/servers.js b/servers.js index 70076a1..cf4a612 100644 --- a/servers.js +++ b/servers.js @@ -20,12 +20,20 @@ Servers.create = function(greenlock) { servers.httpServer = function(defaultApp) { if (_httpServer) { + if (defaultApp) { + console.error("error: can only call httpServer(app) once"); + process.exit(1); + } return _httpServer; } if (!defaultApp) { defaultApp = require("redirect-https")(); } + // HEADERS SENT DEBUG NOTE #1 + // As seen above, it's only possible to create the server once. + // It always gets the http middleware, it always gets a single default app + // Therefore it seems impossible to be an http.on('connection', app) problem _httpServer = http.createServer(HttpMiddleware.create(greenlock, defaultApp)); _httpServer.once("error", startError);