| 
									
										
										
										
											2019-10-26 23:52:19 -06:00
										 |  |  | "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"); | 
					
						
							| 
									
										
										
										
											2019-10-28 01:06:43 -06:00
										 |  |  | var cluster = require("cluster"); | 
					
						
							| 
									
										
										
										
											2019-10-26 23:52:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-28 03:43:42 -06:00
										 |  |  | Servers.create = function(greenlock) { | 
					
						
							| 
									
										
										
										
											2019-11-01 15:14:07 -06:00
										 |  |  |     var servers = {}; | 
					
						
							|  |  |  |     var _httpServer; | 
					
						
							|  |  |  |     var _httpsServer; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function startError(e) { | 
					
						
							|  |  |  |         explainError(e); | 
					
						
							|  |  |  |         process.exit(1); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     servers.httpServer = function(defaultApp) { | 
					
						
							|  |  |  |         if (_httpServer) { | 
					
						
							| 
									
										
										
										
											2019-11-02 20:39:59 -06:00
										 |  |  |             if (defaultApp) { | 
					
						
							|  |  |  |                 console.error("error: can only call httpServer(app) once"); | 
					
						
							|  |  |  |                 process.exit(1); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-11-01 15:14:07 -06:00
										 |  |  |             return _httpServer; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-01 22:55:39 -06:00
										 |  |  |         if (!defaultApp) { | 
					
						
							|  |  |  |             defaultApp = require("redirect-https")(); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-11-02 20:39:59 -06:00
										 |  |  |         // 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
 | 
					
						
							| 
									
										
										
										
											2019-11-01 15:14:07 -06:00
										 |  |  |         _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 + " ") || ""; | 
					
						
							| 
									
										
										
										
											2019-11-01 22:55:39 -06:00
										 |  |  |             var plainServer = servers.httpServer(); | 
					
						
							| 
									
										
										
										
											2019-11-01 15:14:07 -06:00
										 |  |  |             var plainAddr = "0.0.0.0"; | 
					
						
							|  |  |  |             var plainPort = 80; | 
					
						
							|  |  |  |             plainServer.listen(plainPort, plainAddr, function() { | 
					
						
							|  |  |  |                 console.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() { | 
					
						
							|  |  |  |                     console.info(idstr + "Listening on", secureAddr + ":" + securePort, "for secure traffic"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     plainServer.removeListener("error", startError); | 
					
						
							|  |  |  |                     secureServer.removeListener("error", startError); | 
					
						
							|  |  |  |                     resolve(); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return servers; | 
					
						
							| 
									
										
										
										
											2019-10-26 23:52:19 -06:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function explainError(e) { | 
					
						
							| 
									
										
										
										
											2019-11-01 15:14:07 -06:00
										 |  |  |     console.error(); | 
					
						
							|  |  |  |     console.error("Error: " + e.message); | 
					
						
							|  |  |  |     if ("EACCES" === e.errno) { | 
					
						
							|  |  |  |         console.error("You don't have prmission to access '" + e.address + ":" + e.port + "'."); | 
					
						
							|  |  |  |         console.error('You probably need to use "sudo" or "sudo setcap \'cap_net_bind_service=+ep\' $(which node)"'); | 
					
						
							|  |  |  |     } else if ("EADDRINUSE" === e.errno) { | 
					
						
							|  |  |  |         console.error("'" + e.address + ":" + e.port + "' is already being used by some other program."); | 
					
						
							|  |  |  |         console.error("You probably need to stop that program or restart your computer."); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         console.error(e.code + ": '" + e.address + ":" + e.port + "'"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     console.error(); | 
					
						
							| 
									
										
										
										
											2019-10-26 23:52:19 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-28 03:43:42 -06:00
										 |  |  | function wrapDefaultSniCallback(greenlock, secureOpts) { | 
					
						
							| 
									
										
										
										
											2019-11-01 15:14:07 -06:00
										 |  |  |     // 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
 | 
					
						
							|  |  |  |     /* | 
					
						
							| 
									
										
										
										
											2019-10-26 23:52:19 -06:00
										 |  |  | 		if (!secureOpts.SNICallback) { | 
					
						
							|  |  |  | 			secureOpts.SNICallback = function(servername, cb) { | 
					
						
							|  |  |  | 				cb(null, null); | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  |   */ | 
					
						
							| 
									
										
										
										
											2019-11-01 15:14:07 -06:00
										 |  |  |     if (secureOpts.SNICallback) { | 
					
						
							|  |  |  |         console.warn(); | 
					
						
							|  |  |  |         console.warn("[warning] Ignoring the given tlsOptions.SNICallback function."); | 
					
						
							|  |  |  |         console.warn(); | 
					
						
							|  |  |  |         console.warn("          We're very open to implementing support for this,"); | 
					
						
							|  |  |  |         console.warn("          we just don't understand the use case yet."); | 
					
						
							|  |  |  |         console.warn("          Please open an issue to discuss. We'd love to help."); | 
					
						
							|  |  |  |         console.warn(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO greenlock.servername for workers
 | 
					
						
							|  |  |  |     secureOpts.SNICallback = sni.create(greenlock, secureOpts); | 
					
						
							|  |  |  |     return secureOpts; | 
					
						
							| 
									
										
										
										
											2019-10-26 23:52:19 -06:00
										 |  |  | } |