1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
|
var zlib = require('zlib');
var fs = require('fs');
var stream = require('stream');
var util = require('util');
var path = require('path');
var Pend = require('pend');
var findit = require('findit2');
var mime = require('mime');
var url = require('url');
var StreamSink = require('streamsink');
var crypto = require('crypto');
module.exports = createGzipStaticMiddleware;
function createGzipStaticMiddleware(options, cb) {
options = options || {};
var dir = options.dir || "public";
var ignoreFile = options.ignoreFile || defaultIgnoreFile;
var aliases = options.aliases || [['/', '/index.html']];
var followSymlinks = (options.followSymlinks == null) ? true : !!options.followSymlinks;
var cache = {};
var pend = new Pend();
var walker = findit(dir, {followSymlinks: followSymlinks});
walker.on('error', function(err) {
walker.stop();
cb(err);
});
walker.on('file', function(file, stat, linkPath) {
var usePath = linkPath || file;
if (ignoreFile(usePath)) return;
var relName = '/' + path.relative(dir, usePath);
var sink = new StreamSink();
var inStream = fs.createReadStream(file);
var cacheObj;
cache[relName] = cacheObj = {
sink: sink,
mime: mime.lookup(relName),
mtime: stat.mtime,
hash: null,
};
var gzipPendCb, hashPendCb;
inStream.on('error', function(err) {
if (err.code === 'EISDIR') {
delete cache[relName];
gzipPendCb();
hashPendCb();
} else {
walker.stop();
gzipPendCb(err);
hashPendCb(err);
}
});
pend.go(function(cb) {
gzipPendCb = cb;
inStream.pipe(zlib.createGzip()).pipe(sink);
sink.on('finish', cb);
});
pend.go(function(cb) {
hashPendCb = cb;
var hashSink = new StreamSink();
inStream.pipe(crypto.createHash('sha1')).pipe(hashSink);
hashSink.on('finish', function() {
cacheObj.hash = hashSink.toString('base64');
cb();
});
});
});
walker.on('end', function() {
pend.wait(function(err) {
if (err) return cb(err);
aliases.forEach(function(alias) {
cache[alias[0]] = cache[alias[1]];
});
cb(null, middleware);
});
function middleware(req, resp, next) {
var parsedUrl = url.parse(req.url);
var c = cache[parsedUrl.pathname];
if (!c) return next();
if (req.headers['if-none-match'] === c.hash) {
resp.statusCode = 304;
resp.end();
return;
}
var ifModifiedSince = new Date(req.headers['if-modified-since']);
if (!isNaN(ifModifiedSince) && c.mtime <= ifModifiedSince) {
resp.statusCode = 304;
resp.end();
return;
}
var sink = c.sink;
resp.setHeader('Content-Type', c.mime);
resp.setHeader('Cache-Control', 'max-age=0, must-revalidate');
resp.setHeader('ETag', c.hash);
if (req.headers['accept-encoding'] == null) {
sink.createReadStream().pipe(zlib.createGunzip()).pipe(resp);
} else {
resp.setHeader('Content-Encoding', 'gzip');
sink.createReadStream().pipe(resp);
}
}
});
}
function defaultIgnoreFile(file) {
var basename = path.basename(file);
return /^\./.test(basename) || /~$/.test(basename);
}
|