Skip to content
Closed
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ 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 options is set.

##### extensions

Expand All @@ -70,9 +70,11 @@ in preferred order.

##### lastModified

Enable or disable `Last-Modified` header, defaults to true. Uses the file
Enable or disable `Last-Modified` header, defaults to `true`. Uses the file
system's last modified value.

If the `transform` option is set then the default is `false`.

##### maxAge

Provide a max-age in milliseconds for http caching, defaults to 0.
Expand All @@ -83,6 +85,26 @@ 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(...)
}
```

With transform the last-modified and etag defaults 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:
Expand Down
23 changes: 19 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,13 @@ function SendStream(req, path, options) {
this.path = path;
this.options = options;

this._transform = typeof options.transform === 'function'
? options.transform
: undefined

this._etag = options.etag !== undefined
? Boolean(options.etag)
: true
: (this._transform !== undefined ? false : true)

this._dotfiles = options.dotfiles !== undefined
? options.dotfiles
Expand Down Expand Up @@ -114,7 +118,7 @@ function SendStream(req, path, options) {

this._lastModified = options.lastModified !== undefined
? Boolean(options.lastModified)
: true
: (this._transform !== undefined ? false : true)

this._maxage = options.maxAge || options.maxage
this._maxage = typeof this._maxage === 'string'
Expand Down Expand Up @@ -553,7 +557,12 @@ SendStream.prototype.send = function(path, stat){
}

// 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();
Expand Down Expand Up @@ -651,9 +660,15 @@ 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
onFinished(res, function onfinished(){
finished = true;
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"after": "0.8.1",
"istanbul": "0.3.5",
"mocha": "~2.1.0",
"supertest": "~0.15.0"
"supertest": "~0.15.0",
"replacestream": "~1.0.2"
},
"files": [
"HISTORY.md",
Expand Down
45 changes: 45 additions & 0 deletions test/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ var http = require('http');
var path = require('path');
var request = require('supertest');
var send = require('..')
var replaceStream = require('replacestream')

// test server

Expand Down Expand Up @@ -1103,6 +1104,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){
Expand Down