diff --git a/src/index.js b/src/index.js index 312cd1317..36dd1997d 100644 --- a/src/index.js +++ b/src/index.js @@ -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') { @@ -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; @@ -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; diff --git a/test/main.js b/test/main.js index 13ba188ba..a8e2d0eab 100644 --- a/test/main.js +++ b/test/main.js @@ -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(); diff --git a/test/utils/server.js b/test/utils/server.js index f01d15b78..a84efb8b0 100644 --- a/test/utils/server.js +++ b/test/utils/server.js @@ -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');