diff --git a/index.js b/index.js index 8a3b358..8da4d40 100644 --- a/index.js +++ b/index.js @@ -5,16 +5,22 @@ const mimicResponse = require('mimic-response'); module.exports = response => { // TODO: Use Array#includes when targeting Node.js 6 - if (['gzip', 'deflate'].indexOf(response.headers['content-encoding']) === -1) { + if (['gzip', 'deflate', 'br'].indexOf(response.headers['content-encoding']) === -1) { return response; } - const unzip = zlib.createUnzip(); + const isBrotli = response.headers['content-encoding'] === 'br'; + + if (isBrotli && typeof zlib.createBrotliDecompress !== 'function') { + return response; + } + + const decompress = isBrotli ? zlib.createBrotliDecompress() : zlib.createUnzip(); const stream = new PassThrough(); mimicResponse(response, stream); - unzip.on('error', err => { + decompress.on('error', err => { // Ignore empty response if (err.code === 'Z_BUF_ERROR') { stream.end(); @@ -24,7 +30,7 @@ module.exports = response => { stream.emit('error', err); }); - response.pipe(unzip).pipe(stream); + response.pipe(decompress).pipe(stream); return stream; }; diff --git a/readme.md b/readme.md index 1b98767..0216623 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ > Decompress a HTTP response if needed -Decompresses the [response](https://nodejs.org/api/http.html#http_class_http_incomingmessage) from [`http.request`](https://nodejs.org/api/http.html#http_http_request_options_callback) if it's gzipped or deflated, otherwise just passes it through. +Decompresses the [response](https://nodejs.org/api/http.html#http_class_http_incomingmessage) from [`http.request`](https://nodejs.org/api/http.html#http_http_request_options_callback) if it's gzipped, deflated or compressed with Brotli, otherwise just passes it through. Used by [`got`](https://github.com/sindresorhus/got). diff --git a/test/test.js b/test/test.js index aef5cda..486bb7c 100644 --- a/test/test.js +++ b/test/test.js @@ -29,6 +29,13 @@ test.before('setup', async () => { res.end(await zlibP.deflate(fixture)); }); + s.on('/brotli', async (req, res) => { + res.statusCode = 200; + res.setHeader('content-type', 'text/plain'); + res.setHeader('content-encoding', 'br'); + res.end(await zlibP.brotliCompress(fixture)); + }); + s.on('/missing-data', async (req, res) => { res.statusCode = 200; res.setHeader('content-encoding-type', 'text/plain'); @@ -75,6 +82,19 @@ test('decompress deflated content', async t => { t.is(await getStream(res), fixture); }); +if (typeof zlib.brotliCompress === 'function') { + test('decompress brotli content', async t => { + const res = m(await httpGetP(`${s.url}/brotli`)); + + t.is(typeof res.httpVersion, 'string'); + t.truthy(res.headers); + + res.setEncoding('utf8'); + + t.is(await getStream(res), fixture); + }); +} + test('ignore missing data', async t => { const res = m(await httpGetP(`${s.url}/missing-data`));