Skip to content

Commit

Permalink
fix: fixed Brotli decompression; (#5353)
Browse files Browse the repository at this point in the history
test: added decompression tests;
fix: added legacy `x-gzip` & `x-compress` encoding types;
  • Loading branch information
DigitalBrainJS committed Dec 7, 2022
1 parent 56e9ca1 commit 1e58a65
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 31 deletions.
9 changes: 8 additions & 1 deletion lib/adapters/http.js
Expand Up @@ -23,6 +23,11 @@ import EventEmitter from 'events';
const zlibOptions = {
flush: zlib.constants.Z_SYNC_FLUSH,
finishFlush: zlib.constants.Z_SYNC_FLUSH
};

const brotliOptions = {
flush: zlib.constants.BROTLI_OPERATION_FLUSH,
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH
}

const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
Expand Down Expand Up @@ -417,7 +422,9 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
switch (res.headers['content-encoding']) {
/*eslint default-case:0*/
case 'gzip':
case 'x-gzip':
case 'compress':
case 'x-compress':
case 'deflate':
// add the unzipper to the body stream processing pipeline
streams.push(zlib.createUnzip(zlibOptions));
Expand All @@ -427,7 +434,7 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
break;
case 'br':
if (isBrotliSupported) {
streams.push(zlib.createBrotliDecompress(zlibOptions));
streams.push(zlib.createBrotliDecompress(brotliOptions));
delete res.headers['content-encoding'];
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/platform/browser/index.js
Expand Up @@ -43,6 +43,7 @@ const isStandardBrowserEnv = (() => {
const isStandardBrowserWebWorkerEnv = (() => {
return (
typeof WorkerGlobalScope !== 'undefined' &&
// eslint-disable-next-line no-undef
self instanceof WorkerGlobalScope &&
typeof self.importScripts === 'function'
);
Expand Down
106 changes: 76 additions & 30 deletions test/unit/adapters/http.js
Expand Up @@ -34,6 +34,8 @@ function setTimeoutAsync(ms) {
const pipelineAsync = util.promisify(stream.pipeline);
const finishedAsync = util.promisify(stream.finished);
const gzip = util.promisify(zlib.gzip);
const deflate = util.promisify(zlib.deflate);
const brotliCompress = util.promisify(zlib.brotliCompress);

function toleranceRange(positive, negative) {
const p = (1 + 1 / positive);
Expand Down Expand Up @@ -432,7 +434,7 @@ describe('supports http with nodejs', function () {
});
});

describe('compression', () => {
describe('compression', async () => {
it('should support transparent gunzip', function (done) {
var data = {
firstName: 'Fred',
Expand All @@ -455,17 +457,17 @@ describe('supports http with nodejs', function () {
});
});

it('should support gunzip error handling', async () => {
server = await startHTTPServer((req, res) => {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Encoding', 'gzip');
res.end('invalid response');
});
it('should support gunzip error handling', async () => {
server = await startHTTPServer((req, res) => {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Encoding', 'gzip');
res.end('invalid response');
});

await assert.rejects(async ()=> {
await axios.get(LOCAL_SERVER_URL);
})
});
await assert.rejects(async () => {
await axios.get(LOCAL_SERVER_URL);
})
});

it('should support disabling automatic decompression of response data', function(done) {
var data = 'Test data';
Expand All @@ -488,32 +490,76 @@ describe('supports http with nodejs', function () {
});
});

it('should properly handle empty responses without Z_BUF_ERROR throwing', async () => {
this.timeout(10000);
describe('algorithms', ()=> {
const responseBody ='str';

for (const [type, zipped] of Object.entries({
gzip: gzip(responseBody),
compress: gzip(responseBody),
deflate: deflate(responseBody),
br: brotliCompress(responseBody)
})) {
describe(`${type} decompression`, async () => {
it(`should support decompression`, async () => {
server = await startHTTPServer(async (req, res) => {
res.setHeader('Content-Encoding', type);
res.end(await zipped);
});

server = await startHTTPServer((req, res) => {
res.setHeader('Content-Encoding', 'gzip');
res.end();
});
const {data} = await axios.get(LOCAL_SERVER_URL);

await axios.get(LOCAL_SERVER_URL);
});
assert.strictEqual(data, responseBody);
});

it('should not fail if response content-length header is missing', async () => {
this.timeout(10000);
it(`should not fail if response content-length header is missing (${type})`, async () => {
server = await startHTTPServer(async (req, res) => {
res.setHeader('Content-Encoding', type);
res.removeHeader('Content-Length');
res.end(await zipped);
});

const str = 'zipped';
const zipped = await gzip(str);
const {data} = await axios.get(LOCAL_SERVER_URL);

server = await startHTTPServer((req, res) => {
res.setHeader('Content-Encoding', 'gzip');
res.removeHeader('Content-Length');
res.end(zipped);
});
assert.strictEqual(data, responseBody);
});

it('should not fail with chunked responses (without Content-Length header)', async () => {
server = await startHTTPServer(async (req, res) => {
res.setHeader('Content-Encoding', type);
res.setHeader('Transfer-Encoding', 'chunked');
res.removeHeader('Content-Length');
res.write(await zipped);
res.end();
});

const {data} = await axios.get(LOCAL_SERVER_URL);

assert.strictEqual(data, responseBody);
});

it('should not fail with an empty response without content-length header (Z_BUF_ERROR)', async () => {
server = await startHTTPServer((req, res) => {
res.setHeader('Content-Encoding', type);
res.removeHeader('Content-Length');
res.end();
});

const {data} = await axios.get(LOCAL_SERVER_URL);

const {data} = await axios.get(LOCAL_SERVER_URL);
assert.strictEqual(data, '');
});

it('should not fail with an empty response with content-length header (Z_BUF_ERROR)', async () => {
server = await startHTTPServer((req, res) => {
res.setHeader('Content-Encoding', type);
res.end();
});

await axios.get(LOCAL_SERVER_URL);
});
});
}

assert.strictEqual(data, str);
});
});

Expand Down

0 comments on commit 1e58a65

Please sign in to comment.