diff --git a/README.md b/README.md index 3586060..c0d15c9 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,9 @@ with a dot, for backward-compatibility. ##### etag -Enable or disable etag generation, defaults to true. +Enable or disable etag generation. + +Defaults to `true`, unless the `transform` option is set. ##### extensions @@ -70,8 +72,10 @@ in preferred order. ##### lastModified -Enable or disable `Last-Modified` header, defaults to true. Uses the file -system's last modified value. +Enable or disable `Last-Modified` header. Uses the file system's last modified +value. + +Defaults to `true`, unless the `transform` option is set. ##### maxAge @@ -83,6 +87,30 @@ This can also be a string accepted by the Serve files relative to `path`. +##### transform + +A function that consumes the file stream and produces a new (transformed) +stream: + +```javascript +function(stream) {return stream.pipe(replaceStream('tobi', 'peter'))} +``` + +Multiple transformations are possible: + +```javascript +function(stream) { + return stream + .pipe(replaceStream('tobi', 'peter')) + .pipe(replaceStream('peter', 'hans')) + .pipe(...) +} +``` + +When a transform is specified, the `lastModified` and `etag` options default to +`false`, but can be overridden when a transform on the file's stream is expected +to always generate the same result. + ### Events The `SendStream` is an event emitter and will emit the following events: diff --git a/index.js b/index.js index 3510989..557035e 100644 --- a/index.js +++ b/index.js @@ -87,9 +87,13 @@ function SendStream(req, path, options) { this.path = path this.req = req + this._transform = typeof opts.transform === 'function' + ? options.transform + : undefined + this._etag = opts.etag !== undefined ? Boolean(opts.etag) - : true + : (this._transform !== undefined ? false : true) this._dotfiles = opts.dotfiles !== undefined ? opts.dotfiles @@ -120,7 +124,7 @@ function SendStream(req, path, options) { this._lastModified = opts.lastModified !== undefined ? Boolean(opts.lastModified) - : true + : (this._transform !== undefined ? false : true) this._maxage = opts.maxAge || opts.maxage this._maxage = typeof this._maxage === 'string' @@ -592,7 +596,12 @@ SendStream.prototype.send = function(path, stat){ opts.end = Math.max(offset, offset + len - 1) // content-length - res.setHeader('Content-Length', len); + if(this._transform === undefined){ + res.setHeader('Content-Length', len); + } else { + //we don't know the content-length of the transformed data beforehand + res.setHeader('Transfer-Encoding', 'chunked'); + } // HEAD support if ('HEAD' == req.method) return res.end(); @@ -691,6 +700,9 @@ SendStream.prototype.stream = function(path, options){ // pipe var stream = fs.createReadStream(path, options); this.emit('stream', stream); + if(this._transform !== undefined) { + stream = this._transform(stream); + } stream.pipe(res); // response finished, done with the fd diff --git a/package.json b/package.json index 07eb51f..61f2133 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "after": "0.8.1", "istanbul": "0.3.9", "mocha": "2.2.5", + "replacestream": "0.1.3", "supertest": "1.0.1" }, "files": [ diff --git a/test/send.js b/test/send.js index 0548c91..243d645 100644 --- a/test/send.js +++ b/test/send.js @@ -6,8 +6,9 @@ var assert = require('assert'); var fs = require('fs'); var http = require('http'); var path = require('path'); +var replaceStream = require('replacestream'); var request = require('supertest'); -var send = require('..') +var send = require('..'); // test server @@ -1172,6 +1173,50 @@ describe('send(file, options)', function(){ }) }) + describe('transform', function(){ + it('should transform the file contents', function(done){ + var app = http.createServer(function(req, res){ + send(req, 'test/fixtures/name.txt', {transform: function(stream) {return stream.pipe(replaceStream('tobi', 'peter'))}}) + .pipe(res) + }); + + request(app) + .get('/name.txt') + .expect(shouldNotHaveHeader('Last-Modified')) + .expect(shouldNotHaveHeader('ETag')) + .expect(200, "peter", done) + }) + + it('should be possible to do mulitple transformations', function(done){ + var transformFunc = function(stream) { + return stream + .pipe(replaceStream('tobi', 'peter')) + .pipe(replaceStream('peter', 'hans')) + } + + var app = http.createServer(function(req, res){ + send(req, 'test/fixtures/name.txt', {transform: transformFunc}) + .pipe(res) + }); + + request(app) + .get('/name.txt') + .expect(200, "hans", done) + }) + + it('should be able to override last modified', function(done){ + var app = http.createServer(function(req, res){ + send(req, 'test/fixtures/name.txt', {lastModified: true, transform: function(stream) {return stream.pipe(replaceStream('tobi', 'peter'))}}) + .pipe(res) + }); + + request(app) + .get('/name.txt') + .expect('last-modified', dateRegExp) + .expect(200, "peter", done) + }) + }) + describe('root', function(){ describe('when given', function(){ it('should join root', function(done){