Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow custom compression for custom Accept-Encoding Fixes #59 #62

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
22 changes: 22 additions & 0 deletions README.md
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
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
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
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