From 9f3cfc11c67d1f6d980a9e8662eb005fa2262dd0 Mon Sep 17 00:00:00 2001 From: Maciej Goszczycki Date: Thu, 24 Sep 2020 01:05:00 +0200 Subject: [PATCH 1/4] Handle zero-length OK deflate responses --- src/index.js | 13 ++++++++++++- test/main.js | 11 +++++++++++ test/utils/server.js | 7 +++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 312cd1317..102954c98 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(), err => { + if (err) { + reject(err); + } + }); // see https://github.com/nodejs/node/pull/29376 /* c8 ignore next 3 */ if (process.version < 'v12.10') { @@ -299,6 +303,13 @@ export default async function fetch(url, options_) { 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. + if (!response) { + 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'); From 377e09bb2132b6e242375908bcf2a83f04f99aee Mon Sep 17 00:00:00 2001 From: Maciej Goszczycki Date: Fri, 25 Sep 2020 03:06:28 +0200 Subject: [PATCH 2/4] Also handle pump callback in the deflate branch --- src/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 102954c98..cba39b306 100644 --- a/src/index.js +++ b/src/index.js @@ -295,7 +295,11 @@ 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(), err => { + if (err) { + reject(err); + } + }); 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); From e09bc8d258b5c204fb4c7f1c293e6da41cb94d16 Mon Sep 17 00:00:00 2001 From: Maciej Goszczycki Date: Fri, 12 Mar 2021 03:22:41 +0100 Subject: [PATCH 3/4] Update the use of pump everywhere and link to the original PR --- src/index.js | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/index.js b/src/index.js index cba39b306..c7e88bef9 100644 --- a/src/index.js +++ b/src/index.js @@ -285,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), err => { + if (err) { + reject(err); + } + }); response = new Response(body, responseOptions); resolve(response); return; @@ -302,13 +306,26 @@ export default async function fetch(url, options_) { }); 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(), err => { + if (err) { + reject(err); + } + }); + } else { + body = pump(body, zlib.createInflateRaw(), err => { + if (err) { + reject(err); + } + }); + } 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. + // 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); @@ -319,7 +336,11 @@ export default async function fetch(url, options_) { // For br if (codings === 'br') { - body = pump(body, zlib.createBrotliDecompress(), reject); + body = pump(body, zlib.createBrotliDecompress(), err => { + if (err) { + reject(err); + } + }); response = new Response(body, responseOptions); resolve(response); return; From 9aa222f5e107f5fb27033f3d13da007e9c50a6b6 Mon Sep 17 00:00:00 2001 From: Maciej Goszczycki Date: Sun, 16 Jan 2022 23:20:32 +0100 Subject: [PATCH 4/4] Address lints --- src/index.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/index.js b/src/index.js index c7e88bef9..36dd1997d 100644 --- a/src/index.js +++ b/src/index.js @@ -235,9 +235,9 @@ export default async function fetch(url, options_) { }); } - let body = pump(response_, new PassThrough(), err => { - if (err) { - reject(err); + let body = pump(response_, new PassThrough(), error => { + if (error) { + reject(error); } }); // see https://github.com/nodejs/node/pull/29376 @@ -285,9 +285,9 @@ export default async function fetch(url, options_) { // For gzip if (codings === 'gzip' || codings === 'x-gzip') { - body = pump(body, zlib.createGunzip(zlibOptions), err => { - if (err) { - reject(err); + body = pump(body, zlib.createGunzip(zlibOptions), error => { + if (error) { + reject(error); } }); response = new Response(body, responseOptions); @@ -299,23 +299,23 @@ 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(), err => { - if (err) { - reject(err); + const raw = pump(response_, new PassThrough(), error => { + if (error) { + reject(error); } }); raw.once('data', chunk => { // See http://stackoverflow.com/questions/37519828 if ((chunk[0] & 0x0F) === 0x08) { - body = pump(body, zlib.createInflate(), err => { - if (err) { - reject(err); + body = pump(body, zlib.createInflate(), error => { + if (error) { + reject(error); } }); } else { - body = pump(body, zlib.createInflateRaw(), err => { - if (err) { - reject(err); + body = pump(body, zlib.createInflateRaw(), error => { + if (error) { + reject(error); } }); } @@ -336,9 +336,9 @@ export default async function fetch(url, options_) { // For br if (codings === 'br') { - body = pump(body, zlib.createBrotliDecompress(), err => { - if (err) { - reject(err); + body = pump(body, zlib.createBrotliDecompress(), error => { + if (error) { + reject(error); } }); response = new Response(body, responseOptions);