Skip to content

Commit

Permalink
Handle zero-length OK deflate responses (master) (#965)
Browse files Browse the repository at this point in the history
* Handle zero-length OK deflate responses

* Also handle pump callback in the deflate branch

* Update the use of pump everywhere and link to the original PR

* Address lints
  • Loading branch information
nsmaciej committed Jan 16, 2022
1 parent 92dee09 commit dd2cea4
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 5 deletions.
46 changes: 41 additions & 5 deletions src/index.js
Expand Up @@ -235,7 +235,11 @@ export default async function fetch(url, options_) {
});
}

let body = pump(response_, new PassThrough(), reject);
let body = pump(response_, new PassThrough(), error => {
if (error) {
reject(error);
}
});
// see https://github.com/nodejs/node/pull/29376
/* c8 ignore next 3 */
if (process.version < 'v12.10') {
Expand Down Expand Up @@ -281,7 +285,11 @@ export default async function fetch(url, options_) {

// For gzip
if (codings === 'gzip' || codings === 'x-gzip') {
body = pump(body, zlib.createGunzip(zlibOptions), reject);
body = pump(body, zlib.createGunzip(zlibOptions), error => {
if (error) {
reject(error);
}
});
response = new Response(body, responseOptions);
resolve(response);
return;
Expand All @@ -291,20 +299,48 @@ export default async function fetch(url, options_) {
if (codings === 'deflate' || codings === 'x-deflate') {
// Handle the infamous raw deflate response from old servers
// a hack for old IIS and Apache servers
const raw = pump(response_, new PassThrough(), reject);
const raw = pump(response_, new PassThrough(), error => {
if (error) {
reject(error);
}
});
raw.once('data', chunk => {
// See http://stackoverflow.com/questions/37519828
body = (chunk[0] & 0x0F) === 0x08 ? pump(body, zlib.createInflate(), reject) : pump(body, zlib.createInflateRaw(), reject);
if ((chunk[0] & 0x0F) === 0x08) {
body = pump(body, zlib.createInflate(), error => {
if (error) {
reject(error);
}
});
} else {
body = pump(body, zlib.createInflateRaw(), error => {
if (error) {
reject(error);
}
});
}

response = new Response(body, responseOptions);
resolve(response);
});
raw.once('end', () => {
// Some old IIS servers return zero-length OK deflate responses, so
// 'data' is never emitted. See https://github.com/node-fetch/node-fetch/pull/903
if (!response) {
response = new Response(body, responseOptions);
resolve(response);
}
});
return;
}

// For br
if (codings === 'br') {
body = pump(body, zlib.createBrotliDecompress(), reject);
body = pump(body, zlib.createBrotliDecompress(), error => {
if (error) {
reject(error);
}
});
response = new Response(body, responseOptions);
resolve(response);
return;
Expand Down
11 changes: 11 additions & 0 deletions test/main.js
Expand Up @@ -946,6 +946,17 @@ describe('node-fetch', () => {
});
});

it('should handle empty deflate response', () => {
const url = `${base}empty/deflate`;
return fetch(url).then(res => {
expect(res.headers.get('content-type')).to.equal('text/plain');
return res.text().then(result => {
expect(result).to.be.a('string');
expect(result).to.be.empty;
});
});
});

it('should decompress brotli response', function () {
if (typeof zlib.createBrotliDecompress !== 'function') {
this.skip();
Expand Down
7 changes: 7 additions & 0 deletions test/utils/server.js
Expand Up @@ -179,6 +179,13 @@ export default class TestServer {
});
}

if (p === '/empty/deflate') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Encoding', 'deflate');
res.end();
}

if (p === '/sdch') {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
Expand Down

0 comments on commit dd2cea4

Please sign in to comment.