UNPKG

roster-server

Version:

👾 RosterServer - A domain host router to host multiple HTTPS.

171 lines (149 loc) • 6.05 kB
"use strict"; var Servers = module.exports; var http = require("http"); var HttpMiddleware = require("./http-middleware.js"); var HttpsMiddleware = require("./https-middleware.js"); var sni = require("./sni.js"); var cluster = require("cluster"); var log = require("lemonlog")("greenlock-servers"); Servers.create = function(greenlock) { var servers = {}; var _httpServer; var _httpsServer; function startError(e) { explainError(e); process.exit(1); } servers.httpServer = function(defaultApp) { if (_httpServer) { if (defaultApp) { log.error("Invalid API usage: `httpServer(app)` can only be called 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); return _httpServer; }; var _middlewareApp; servers.http2Server = function(secureOpts, defaultApp) { return servers._httpsServer(secureOpts, defaultApp, function(secureOpts, fn) { secureOpts.allowHTTP1 = true; return require("http2").createSecureServer(secureOpts, fn); }); }; servers.httpsServer = function(secureOpts, defaultApp) { return servers._httpsServer(secureOpts, defaultApp, function(secureOpts, fn) { return require("https").createServer(secureOpts, fn); }); }; servers._httpsServer = function(secureOpts, defaultApp, createSecureServer) { if (defaultApp) { // TODO guard against being set twice? _middlewareApp = defaultApp; } if (_httpsServer) { if (secureOpts && Object.keys(secureOpts).length) { throw new Error("Call glx.httpsServer(tlsOptions) before calling glx.serveApp(app)"); } return _httpsServer; } if (!secureOpts) { secureOpts = {}; } _httpsServer = createSecureServer( wrapDefaultSniCallback(greenlock, secureOpts), HttpsMiddleware.create(greenlock, function(req, res) { if (!_middlewareApp) { throw new Error("Set app with `glx.serveApp(app)` or `glx.httpsServer(tlsOptions, app)`"); } _middlewareApp(req, res); }) ); _httpsServer.once("error", startError); return _httpsServer; }; servers.id = function() { return (cluster.isWorker && cluster.worker.id) || "0"; }; servers.serveApp = function(app) { return new Promise(function(resolve, reject) { if ("function" !== typeof app) { reject( new Error( "glx.serveApp(app) expects a node/express app in the format `function (req, res) { ... }`" ) ); return; } var id = cluster.isWorker && cluster.worker.id; var idstr = (id && "#" + id + " ") || ""; var plainServer = servers.httpServer(); var plainAddr = "0.0.0.0"; var plainPort = 80; plainServer.listen(plainPort, plainAddr, function() { log.info( idstr + "Listening on", plainAddr + ":" + plainPort, "for ACME challenges, and redirecting to HTTPS" ); // TODO fetch greenlock.servername _middlewareApp = app || _middlewareApp; var secureServer = servers.httpsServer(null, app); var secureAddr = "0.0.0.0"; var securePort = 443; secureServer.listen(securePort, secureAddr, function() { log.info(idstr + "Listening on", secureAddr + ":" + securePort, "for secure traffic"); plainServer.removeListener("error", startError); secureServer.removeListener("error", startError); resolve(); }); }); }); }; return servers; }; function explainError(e) { log.error("Server startup error", { code: e.code || e.errno || null, address: e.address || null, port: e.port || null, message: e.message }); if ("EACCES" === e.errno) { log.error("Insufficient permission to bind " + e.address + ":" + e.port + "."); log.error('You probably need to use "sudo" or "sudo setcap \'cap_net_bind_service=+ep\' $(which node)"'); } else if ("EADDRINUSE" === e.errno) { log.error("Address already in use: " + e.address + ":" + e.port + "."); log.error("You probably need to stop that program or restart your computer."); } else { log.error(e.code + ": '" + e.address + ":" + e.port + "'"); } } function wrapDefaultSniCallback(greenlock, secureOpts) { // I'm not sure yet if the original SNICallback // should be called before or after, so I'm just // going to delay making that choice until I have the use case /* if (!secureOpts.SNICallback) { secureOpts.SNICallback = function(servername, cb) { cb(null, null); }; } */ if (secureOpts.SNICallback) { log.warn("Ignoring user-provided tlsOptions.SNICallback; Greenlock-managed SNI callback is required"); log.warn("If you need custom SNI behavior, open an issue to discuss integration support"); } // TODO greenlock.servername for workers secureOpts.SNICallback = sni.create(greenlock, secureOpts); return secureOpts; }