static assets are gzipped and held permanently in memory

This commit is contained in:
Andrew Kelley 2014-03-04 04:58:20 -05:00
parent 0852ab8cff
commit 9768c840b9
2 changed files with 116 additions and 5 deletions

View file

@ -22,6 +22,7 @@ var crypto = require('crypto');
var net = require('net');
var safePath = require('./safe_path');
var multipart = require('connect-multiparty')();
var createGzipStatic = require('./gzip_static');
module.exports = GrooveBasin;
@ -100,9 +101,29 @@ GrooveBasin.prototype.loadConfig = function(cb) {
GrooveBasin.prototype.start = function() {
var self = this;
self.loadConfig(function(err) {
var pend = new Pend();
pend.go(function(cb) {
self.loadConfig(cb);
});
pend.go(function(cb) {
var aliases = [];
createGzipStatic(path.join(__dirname, "../public"), aliases, function(err, middleware) {
if (err) return cb(err);
self.app.use(middleware);
cb();
});
});
pend.go(function(cb) {
var aliases = [['/', '/index.html']];
createGzipStatic(path.join(__dirname, "../src/public"), aliases, function(err, middleware) {
if (err) return cb(err);
self.app.use(middleware);
cb();
});
});
pend.wait(function(err) {
if (err) {
console.error("Error reading config:", err.stack);
console.error(err.stack);
return;
}
@ -110,9 +131,6 @@ GrooveBasin.prototype.start = function() {
self.httpPort = self.config.port;
self.db = levelup(self.config.dbPath);
self.app.use(express.static(path.join(__dirname, '../public')));
self.app.use(express.static(path.join(__dirname, '../src/public')));
self.initializeDownload();
self.initializeUpload();

93
lib/gzip_static.js Normal file
View file

@ -0,0 +1,93 @@
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('findit');
module.exports = createGzipStaticMiddleware;
function createGzipStaticMiddleware(dir, aliases, cb) {
var cache = {};
var pend = new Pend();
var walker = findit(dir);
walker.on('file', function(file) {
if (ignoreFile(file)) return;
var relName = '/' + path.relative(dir, file);
var raw = new Sink();
var gzipped = new Sink();
var inStream = fs.createReadStream(file);
inStream.on('error', function(err) {
if (err.code === 'EISDIR') {
delete cache[relName];
return;
} else {
throw err;
}
});
cache[relName] = {
raw: raw,
gzipped: gzipped,
}
pend.go(function(cb) {
inStream.pipe(raw);
raw.once('finish', cb);
});
pend.go(function(cb) {
inStream.pipe(zlib.createGzip()).pipe(gzipped);
gzipped.once('finish', 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 c = cache[req.url];
if (!c) return next();
var sink;
if (req.headers['accept-encoding'] == null) {
console.log("sent raw");
sink = c.raw;
} else {
console.log("sent gzipped");
sink = c.gzipped;
resp.setHeader('content-encoding', 'gzip');
}
sink.createReadStream().pipe(resp);
}
});
}
util.inherits(Sink, stream.Writable);
function Sink(options) {
stream.Writable.call(this, options);
this.buffer = [];
}
Sink.prototype._write = function(chunk, encoding, callback) {
this.buffer.push(chunk);
callback();
};
Sink.prototype.createReadStream = function(options) {
var s = new stream.Readable(options);
s.buffer = this.buffer;
s._read = function(size) {
for (var i = 0; i < s.buffer.length; i += 1) {
s.push(s.buffer[i]);
}
s.push(null);
};
return s;
};
function ignoreFile(file) {
var basename = path.basename(file);
return /^\./.test(basename) || /~$/.test(file);
}