Skip to content
Open
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,28 @@ The default value is `zlib.Z_DEFAULT_WINDOWBITS`, or `15`.
See [Node.js documentation](http://nodejs.org/api/zlib.html#zlib_memory_usage_tuning)
regarding the usage.

##### compressor

Allows you to specify your own compression stream for a given Content-Encoding.

```js
var opts = {
compressor: {
br: brotliStreamEncoder,
lzma: lzmaStreamEncoder,
},
};
```

Where the custom compressor is a function that returns effectively a transform
stream. ex:

```js
function brotliStreamEncoder (opts) {
return brotli.compressStream(opts);
};
```

#### .filter

The default `filter` function. This is used to construct a custom filter
Expand Down
22 changes: 18 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,17 @@ function compression(options) {

// compression method
var accept = accepts(req)
var method = accept.encoding(['gzip', 'deflate', 'identity'])
var method = null
var acceptableEncodings = ['gzip', 'deflate', 'identity']

// If we support a custom encoding and a custom encoding was requested
if (opts.compressor) {
method = accept.encoding(Object.keys(opts.compressor).concat(acceptableEncodings))
}

if (!method) {
method = accept.encoding(acceptableEncodings)
}

// we really don't prefer deflate
if (method === 'deflate' && accept.encoding(['gzip'])) {
Expand All @@ -188,9 +198,13 @@ function compression(options) {

// compression stream
debug('%s compression', method)
stream = method === 'gzip'
? zlib.createGzip(opts)
: zlib.createDeflate(opts)
if (method === 'gzip') {
stream = zlib.createGzip(opts)
} else if (method === 'deflate') {
stream = zlib.createDeflate(opts)
} else if (opts.compressor && method in opts.compressor) {
stream = opts.compressor[method](opts)
}

// add bufferred listeners to stream
addListeners(stream, stream.on, listeners)
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"devDependencies": {
"istanbul": "0.3.21",
"mocha": "2.3.3",
"supertest": "1.1.0"
"supertest": "1.1.0",
"through": "2.3.8"
},
"files": [
"LICENSE",
Expand Down
79 changes: 79 additions & 0 deletions test/compression.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var bytes = require('bytes');
var crypto = require('crypto');
var http = require('http');
var request = require('supertest');
var through = require('through');

var compression = require('..');

Expand Down Expand Up @@ -478,6 +479,14 @@ describe('compression()', function(){
})
})

function createCompressor () {
return through(function (d) {
this.queue(d.toString().split('').reverse().join(''))
}, function () {
this.queue(null)
})
}

describe('when "Accept-Encoding: deflate, gzip"', function () {
it('should respond with gzip', function (done) {
var server = createServer({ threshold: 0 }, function (req, res) {
Expand All @@ -490,6 +499,76 @@ describe('compression()', function(){
.set('Accept-Encoding', 'deflate, gzip')
.expect('Content-Encoding', 'gzip', done)
})

it('should respond with gzip even when a custom compressor is specified but not requested', function (done) {
var opts = {
threshold: 0,
compressor: {
'bingo': createCompressor
}
}
var server = createServer(opts, function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('hello, world')
})

request(server)
.get('/')
.set('Accept-Encoding', 'gzip, deflate')
.expect('Content-Encoding', 'gzip', done)
})
})

describe('when "Accept-Encoding: custom"', function () {
it('should not use content encoding without a custom compressor function', function (done) {
var server = createServer({ threshold: 0 }, function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('hello, world')
})

request(server)
.get('/')
.set('Accept-Encoding', 'custom')
.expect(shouldNotHaveHeader('Content-Encoding'))
.expect(200, 'hello, world', done)
})

it('should use content encoding with a custom compressor function', function (done) {
var opts = {
threshold: 0,
compressor: {
'bingo': createCompressor
}
}
var server = createServer(opts, function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('hello, world')
})

request(server)
.get('/')
.set('Accept-Encoding', 'bingo, gzip')
.expect('Content-Encoding', 'bingo')
.expect(200, 'dlrow ,olleh', done)
})

it('should obey q= priorities', function (done) {
var opts = {
threshold: 0,
compressor: {
'bingo': createCompressor
}
}
var server = createServer(opts, function (req, res) {
res.setHeader('Content-Type', 'text/plain')
res.end('hello, world')
})

request(server)
.get('/')
.set('Accept-Encoding', 'bingo;q=0.001, gzip')
.expect('Content-Encoding', 'gzip', done)
})
})

describe('when "Cache-Control: no-transform" response header', function () {
Expand Down