hostr
Version:
A simple web server for the current working directory. Used for hello world style web sites hosting only files in current directory structure. Watches files and integrates with LiveReload.
106 lines (82 loc) • 2.67 kB
JavaScript
;
var fs = require('fs');
var url = require('url');
var crypto = require('crypto');
var mime = require('mime-types');
var debug = require('../debug')(process.env.QUIET === 'true');
/**
* Static files route
*
* @param {http.incomingMessage} req A request Object
* @param {http.ServerResponse} res A response Object
* @param {Function} callback A callback, function(err)
*/
module.exports = function(options) {
options = options || {};
var _seed = Math.random();
// Expiration time (sec)
var _expirationTime = options.cacheExpiration || 60;
var _handler = function(req, res, next) {
var resource = url.parse(req.url);
var resourcePathname = resource.pathname || '/';
resourcePathname = resourcePathname.replace(/\/\.\./, "").replace(/\.\.\//, "");
var filePath = process.cwd()+resourcePathname;
if(filePath[filePath.length-1] === '/') filePath += 'index.html';
var startTime = process.hrtime();
fs.stat(filePath, function(err, stat) {
if(err) {
return next(err);
}
// ETag hash
var uniqueHash = crypto.createHash('md5').update(new Buffer([
_seed,
filePath,
(stat.mtime && stat.mtime.getTime())
].join(''))).digest('hex');
var eTag = 'W/"'+uniqueHash+'"';
// Use cache
var headers = req.headers || {};
var matchETag = headers['if-none-match'] === eTag;
var matchExpiration = ((new Date(headers['if-modified-since'])).getTime() - Date.now()) >= 0;
res.setHeader('ETag', eTag);
if(matchExpiration && matchETag) {
res.writeHead(304);
res.end();
return;
}
res.setHeader('Last-Modified', new Date(Date.now() + _expirationTime * 1000));
res.setHeader('Cache-Control', "max-age="+ _expirationTime);
// Interpret mime types
res.setHeader('Content-Type', mime.lookup(filePath));
var readStream = fs.createReadStream(filePath);
readStream.on('open', function() {
readStream.pipe(res);
});
readStream.on('error', function(err) {
if(err && err.code === 'ENOENT') {
return next();
} else {
res.writeHead(500);
res.end();
}
});
});
// Log request
res.on('finish', function() {
var deltaTime = process.hrtime(startTime);
debug.log([
res.statusCode,
req.method,
req.url,
Math.ceil((deltaTime[0] * 1e9 + deltaTime[1]) / 1e6) + 'ms'
].join(' '));
});
};
/**
* Reset seed
*/
_handler.reset = function() {
_seed = Math.random();
};
return _handler;
};