Permalink
Browse files

Adding static path prefix

  • Loading branch information...
1 parent cbdb1df commit 53ae9f0fddc538f3932f12e282da8759deb911eb @serby serby committed Sep 18, 2011
Showing with 127 additions and 29 deletions.
  1. +5 −4 README.md
  2. +34 −25 lib/staticGzip.js
  3. +88 −0 test/prefexTest.js
View
@@ -5,15 +5,15 @@ gzippo pronounced `g-zippo` is a gzip middleware for Connect / expressjs using n
gzippo currently only supports only gzipping static content files however a release is in progress to introduce streaming support.
## Installation
-
+
$ npm install gzippo
### Usage
In your express/connect server setup, use as follows:
-
+
var gzippo = require('gzippo');
-
+
//Replace the static provider with gzippo's
//app.use(express.static(__dirname + '/public'));
app.use(gzippo.staticGzip(__dirname + '/public'));
@@ -23,10 +23,11 @@ Options:
- `contentTypeMatch` - A regular expression tested against the Content-Type header to determine whether the response should be gzipped or not. The default value is `/text|javascript|json/`.
- `maxAge` - cache-control max-age directive, defaulting to 1 day
- `clientMaxAge` - browser cache-control max-age directive, defaulting to 1 week
+- `prefix` - A url prefix. If you want all your static content in a root path such as /resource/. Any url paths not matching will be ignored
Currently the gzipped version is created and stored in memory. This is not final and was done to get a working version
up and about. A version which will gzip text/html after res.render() / res.end() is in progress.
-
+
[node-compress](https://github.com/waveto/node-compress) gzip library is used for gzipping.
Found gzippo helpful? Why don't you [tip us](http://tiptheweb.org/tip/?link=https%3A%2F%2Fgithub.com%2Ftomgallacher%2Fgzippo&title=Tip%20to%20Support%20gzippo) [![Flattr Button](http://api.flattr.com/button/flattr-badge-large.png "Flattr This!")](https://flattr.com/thing/282348/gzippo-node-js-gzip-module "gzippo - node.js gzip module")
View
@@ -1,6 +1,6 @@
/*!
* Tom Gallacher
- *
+ *
* MIT Licensed
*/
@@ -44,7 +44,7 @@ var gzippoCache = {};
/**
* gzip file.
*/
-
+
var gzippo = function(filename, charset, callback) {
var gzip = new compress.Gzip();
gzip.init();
@@ -63,8 +63,9 @@ var gzippo = function(filename, charset, callback) {
*
* - `maxAge` cache-control max-age directive, defaulting to 1 day
* - `clientMaxAge` client cache-control max-age directive, defaulting to 1 week
- * - `contentTypeMatch` - A regular expression tested against the Content-Type header to determine whether the response
+ * - `contentTypeMatch` - A regular expression tested against the Content-Type header to determine whether the response
* should be gzipped or not. The default value is `/text|javascript|json/`.
+ * - `prefix` - A url prefix. If you want all your static content in a root path such as /resource/. Any url paths not matching will be ignored
*
* Examples:
*
@@ -84,40 +85,42 @@ var gzippo = function(filename, charset, callback) {
exports = module.exports = function staticGzip(dirPath, options){
options = options || {};
- var maxAge = options.maxAge || 86400000,
- contentTypeMatch = options.contentTypeMatch || /text|javascript|json/,
- clientMaxAge = options.clientMaxAge || 604800000;
-
+ var
+ maxAge = options.maxAge || 86400000,
+ contentTypeMatch = options.contentTypeMatch || /text|javascript|json/,
+ clientMaxAge = options.clientMaxAge || 604800000,
+ prefix = options.prefix || '';
+
if (!dirPath) throw new Error('You need to provide the directory to your static content.');
if (!contentTypeMatch.test) throw new Error('contentTypeMatch: must be a regular expression.');
return function staticGzip(req, res, next){
var url, filename, contentType, acceptEncoding, charset;
-
+
function pass(name) {
var o = Object.create(options);
o.path = name;
o.maxAge = maxAge;
staticSend(req, res, next, o);
}
-
+
function setHeaders(cacheObj) {
res.setHeader('Content-Type', contentType);
res.setHeader('Content-Encoding', 'gzip');
- res.setHeader('Vary', 'Accept-Encoding');
+ res.setHeader('Vary', 'Accept-Encoding');
res.setHeader('Content-Length', cacheObj.content.length);
res.setHeader('Last-Modified', cacheObj.mtime.toUTCString());
res.setHeader('Date', new Date().toUTCString());
res.setHeader('Expires', new Date(Date.now() + clientMaxAge).toUTCString());
res.setHeader('Cache-Control', 'public, max-age=' + (clientMaxAge / 1000));
res.setHeader('ETag', '"' + cacheObj.content.length + '-' + Number(cacheObj.mtime) + '"');
}
-
+
function sendGzipped(cacheObj) {
setHeaders(cacheObj);
res.end(cacheObj.content, 'binary');
}
-
+
function gzipAndSend(filename, gzipName, mtime) {
gzippo(filename, charset, function(gzippedData) {
gzippoCache[gzipName] = {
@@ -126,17 +129,23 @@ exports = module.exports = function staticGzip(dirPath, options){
'content': gzippedData
};
sendGzipped(gzippoCache[gzipName]);
- });
+ });
}
-
-
+
+
if (req.method !== 'GET') {
return next();
}
-
+
url = parse(req.url);
- filename = path.join(dirPath, url.pathname);
-
+
+ // Allow a url path prefix
+ if (url.pathname.substring(0, prefix.length) !== prefix) {
+ return next();
+ }
+
+ filename = path.join(dirPath, url.pathname.substring(prefix.length));
+
contentType = mime.lookup(filename);
charset = mime.charsets.lookup(contentType);
contentType = contentType + (charset ? '; charset=' + charset : '');
@@ -145,23 +154,23 @@ exports = module.exports = function staticGzip(dirPath, options){
if (!contentTypeMatch.test(contentType)) {
return pass(filename);
}
-
+
if (!~acceptEncoding.indexOf('gzip')) {
return pass(filename);
}
//This is storing in memory for the moment, need to think what the best way to do this.
//Check file is not a directory
-
+
fs.stat(filename, function(err, stat) {
if (err || stat.isDirectory()) {
- return pass(filename);
+ return pass(filename);
}
var base = path.basename(filename),
dir = path.dirname(filename),
gzipName = path.join(dir, base + '.gz');
-
+
if (req.headers['if-modified-since'] &&
gzippoCache[gzipName] &&
+gzippoCache[gzipName].mtime <= new Date(req.headers['if-modified-since']).getTime()) {
@@ -170,12 +179,12 @@ exports = module.exports = function staticGzip(dirPath, options){
res.statusCode = 304;
return res.end();
}
-
- //TODO: check for pre-compressed file
+
+ //TODO: check for pre-compressed file
if (typeof gzippoCache[gzipName] === 'undefined') {
gzipAndSend(filename, gzipName, stat.mtime);
} else {
- if ((gzippoCache[gzipName].mtime < stat.mtime) ||
+ if ((gzippoCache[gzipName].mtime < stat.mtime) ||
((gzippoCache[gzipName].ctime + maxAge) < Date.now())) {
gzipAndSend(filename, gzipName, stat.mtime);
} else {
View
@@ -0,0 +1,88 @@
+
+/**
+ * Module dependencies.
+ */
+
+var staticProvider,
+ assert = require('assert'),
+ should = require('should'),
+ http = require('http'),
+ gzippo = require('../');
+
+try {
+ staticProvider = require('connect');
+} catch (e) {
+ staticProvider = require('express');
+}
+
+/**
+ * Path to ./test/fixtures/
+ */
+
+var fixturesPath = __dirname + '/fixtures';
+
+module.exports = {
+ 'requesting without a prefix succeeds': function() {
+ var app = staticProvider.createServer(
+ gzippo.staticGzip(fixturesPath)
+ );
+
+ assert.response(app,
+ {
+ url: '/user.json',
+ headers: {
+ 'Accept-Encoding':"gzip"
+ }
+ },
+ function(res){
+ var gzippedData = res.body;
+ res.statusCode.should.equal(200);
+ res.headers.should.have.property('content-type', 'application/json');
+ res.headers.should.have.property('content-length', '69');
+ res.headers.should.have.property('content-encoding', 'gzip');
+ }
+ );
+ },
+ 'requesting with a prefix succeeds': function() {
+ var app = staticProvider.createServer(
+ gzippo.staticGzip(fixturesPath, { prefix: '/resource' })
+ );
+
+ assert.response(app,
+ {
+ url: '/resource/user.json',
+ headers: {
+ 'Accept-Encoding':"gzip"
+ }
+ },
+ function(res){
+ var gzippedData = res.body;
+ res.statusCode.should.equal(200);
+ res.headers.should.have.property('content-type', 'application/json');
+ res.headers.should.have.property('content-length', '69');
+ res.headers.should.have.property('content-encoding', 'gzip');
+ }
+ );
+ },
+ 'requesting with a / prefix succeeds': function() {
+ var app = staticProvider.createServer(
+ gzippo.staticGzip(fixturesPath, { prefix: '/'})
+ );
+
+ assert.response(app,
+ {
+ url: '/user.json',
+ headers: {
+ 'Accept-Encoding':"gzip"
+ }
+ },
+ function(res){
+ var gzippedData = res.body;
+ res.statusCode.should.equal(200);
+ res.headers.should.have.property('content-type', 'application/json');
+ res.headers.should.have.property('content-length', '69');
+ res.headers.should.have.property('content-encoding', 'gzip');
+ }
+ );
+ }
+};

0 comments on commit 53ae9f0

Please sign in to comment.