diff --git a/lib/modules/http.js b/lib/modules/http.js index 5cbb5b8..d5fa572 100644 --- a/lib/modules/http.js +++ b/lib/modules/http.js @@ -2,8 +2,7 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { var PromiseA = require('bluebird'); - var express = require('express'); - var app = express(); + var statAsync = PromiseA.promisify(require('fs').stat); var domainMatches = require('../domain-utils').match; var separatePort = require('../domain-utils').separatePort; @@ -119,51 +118,6 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { redirecters[host.port](req, res); } - function respond404(req, res) { - res.writeHead(404); - res.end('Not Found'); - } - - function createStaticRoute(mod) { - var getStaticApp, staticApp; - if (/:hostname/.test(mod.root)) { - staticApp = {}; - getStaticApp = function (hostname) { - if (!staticApp[hostname]) { - staticApp[hostname] = express.static(mod.root.replace(':hostname', hostname)); - } - return staticApp[hostname]; - }; - } - else { - staticApp = express.static(mod.root); - getStaticApp = function () { - return staticApp; - }; - } - - return function (req, res, next) { - if (moduleMatchesHost(req, mod)) { - getStaticApp(separatePort(req.headers.host).host)(req, res, next); - } else { - next(); - } - }; - } - - (conf.http.modules || []).forEach(function (mod) { - if (mod.name === 'static') { - app.use(createStaticRoute(mod)); - } - else if (mod.name !== 'proxy' && mod.name !== 'redirect') { - console.warn('unknown HTTP module', mod); - } - }); - - app.use(respond404); - - var server = require('http').createServer(app); - function emitConnection(server, conn, opts) { server.emit('connection', conn); @@ -190,7 +144,6 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { return emitConnection(acmeServer, conn, opts); } - var httpsRedirectServer; function checkHttps(conn, opts, headers) { if (conf.http.allowInsecure || conn.encrypted) { @@ -297,6 +250,7 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { 'HTTP/1.1 ' + status + ' ' + code , 'Date: ' + (new Date()).toUTCString() , 'Location: ' + to + , 'Connection: close' , 'Content-Length: 0' , '' , '' @@ -304,6 +258,57 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { return true; } + var staticServer; + var staticHandlers = {}; + function serveStatic(req, res) { + var rootDir = req.connection.rootDir; + + if (!staticHandlers[rootDir]) { + staticHandlers[rootDir] = require('express').static(rootDir, { fallthrough: false }); + } + + staticHandlers[rootDir](req, res, function (err) { + if (err) { + res.statusCode = err.statusCode; + } else { + res.statusCode = 404; + } + res.setHeader('Content-Type', 'text/html'); + + if (res.statusCode === 404) { + res.end('File Not Found'); + } else { + res.end(require('http').STATUS_CODES[res.statusCode]); + } + }); + } + function checkStatic(mod, conn, opts, headers) { + if (!moduleMatchesHost(headers, mod)) { + return false; + } + + var rootDir = mod.root.replace(':hostname', separatePort(headers.host).host); + return statAsync(rootDir) + .then(function (stats) { + if (!stats || !stats.isDirectory()) { + return false; + } + + if (!staticServer) { + staticServer = require('http').createServer(serveStatic); + } + conn.rootDir = rootDir; + return emitConnection(staticServer, conn, opts); + }) + .catch(function (err) { + if (err.code !== 'ENOENT') { + console.warn('errored stating', rootDir, 'for serving static files', err); + } + return false; + }) + ; + } + function handleConnection(conn) { var opts = conn.__opts; parseHeaders(conn, opts) @@ -312,19 +317,38 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { if (checkHttps(conn, opts, headers)) { return; } if (checkAdmin(conn, opts, headers)) { return; } - var handled = (conf.http.modules || []).some(function (mod) { - if (mod.name === 'proxy') { - return checkProxy(mod, conn, opts, headers); - } - else if (mod.name === 'redirect') { - return checkRedirect(mod, conn, opts, headers); + var prom = PromiseA.resolve(false); + (conf.http.modules || []).forEach(function (mod) { + prom = prom.then(function (handled) { + if (handled) { + return handled; + } + if (mod.name === 'proxy') { + return checkProxy(mod, conn, opts, headers); + } + if (mod.name === 'redirect') { + return checkRedirect(mod, conn, opts, headers); + } + if (mod.name === 'static') { + return checkStatic(mod, conn, opts, headers); + } + console.warn('unknown HTTP module found', mod); + }); + }); + + prom.then(function (handled) { + if (!handled) { + conn.end([ + 'HTTP/1.1 404 Not Found' + , 'Date: ' + (new Date()).toUTCString() + , 'Content-Type: text/html' + , 'Content-Length: 9' + , 'Connection: close' + , '' + , 'Not Found' + ].join('\r\n')); } }); - if (handled) { - return; - } - - emitConnection(server, conn, opts); }) ; }