From cb6e54ccc369602b5ca7a569eb4628eae0ab98b0 Mon Sep 17 00:00:00 2001 From: DigitalBrainJS Date: Thu, 10 Nov 2022 21:54:27 +0200 Subject: [PATCH] Fixed Z_BUF_ERROR when content-encoding is set but the response body is empty; Fixed download progress capturing for compressed responses; --- lib/adapters/http.js | 36 +++++++------- test/unit/adapters/http.js | 99 ++++++++++++++++++++++---------------- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/lib/adapters/http.js b/lib/adapters/http.js index b561645d2f..5dbd97c8e9 100755 --- a/lib/adapters/http.js +++ b/lib/adapters/http.js @@ -373,6 +373,23 @@ export default function httpAdapter(config) { const streams = [res]; + const responseLength = +res.headers['content-length']; + + if (onDownloadProgress) { + const transformStream = new AxiosTransformStream({ + length: utils.toFiniteNumber(responseLength), + maxRate: utils.toFiniteNumber(maxDownloadRate) + }); + + onDownloadProgress && transformStream.on('progress', progress => { + onDownloadProgress(Object.assign(progress, { + download: true + })); + }); + + streams.push(transformStream); + } + // uncompress the response body transparently if required let responseStream = res; @@ -383,7 +400,7 @@ export default function httpAdapter(config) { if (config.decompress !== false) { // if no content, but headers still say that it is encoded, // remove the header not confuse downstream operations - if (data && data.length === 0 && res.headers['content-encoding']) { + if ((!responseLength || res.statusCode === 204) && res.headers['content-encoding']) { delete res.headers['content-encoding']; } @@ -406,23 +423,6 @@ export default function httpAdapter(config) { } } - if (onDownloadProgress) { - const responseLength = +res.headers['content-length']; - - const transformStream = new AxiosTransformStream({ - length: utils.toFiniteNumber(responseLength), - maxRate: utils.toFiniteNumber(maxDownloadRate) - }); - - onDownloadProgress && transformStream.on('progress', progress => { - onDownloadProgress(Object.assign(progress, { - download: true - })); - }); - - streams.push(transformStream); - } - responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0]; const offListeners = stream.finished(responseStream, () => { diff --git a/test/unit/adapters/http.js b/test/unit/adapters/http.js index fba276d141..92a11a75e0 100644 --- a/test/unit/adapters/http.js +++ b/test/unit/adapters/http.js @@ -47,9 +47,14 @@ var noop = ()=> {}; const LOCAL_SERVER_URL = 'http://localhost:4444'; -function startHTTPServer({useBuffering= false, rate = undefined, port = 4444} = {}) { +function startHTTPServer(options) { + + const {handler, useBuffering = false, rate = undefined, port = 4444} = typeof options === 'function' ? { + handler: options + } : options || {}; + return new Promise((resolve, reject) => { - http.createServer(async function (req, res) { + http.createServer(handler || async function (req, res) { try { req.headers['content-length'] && res.setHeader('content-length', req.headers['content-length']); @@ -426,58 +431,71 @@ describe('supports http with nodejs', function () { }); }); - it('should support transparent gunzip', function (done) { - var data = { - firstName: 'Fred', - lastName: 'Flintstone', - emailAddr: 'fred@example.com' - }; + describe('compression', () => { + it('should support transparent gunzip', function (done) { + var data = { + firstName: 'Fred', + lastName: 'Flintstone', + emailAddr: 'fred@example.com' + }; - zlib.gzip(JSON.stringify(data), function (err, zipped) { + zlib.gzip(JSON.stringify(data), function (err, zipped) { - server = http.createServer(function (req, res) { - res.setHeader('Content-Type', 'application/json'); - res.setHeader('Content-Encoding', 'gzip'); - res.end(zipped); - }).listen(4444, function () { - axios.get('http://localhost:4444/').then(function (res) { - assert.deepEqual(res.data, data); - done(); - }).catch(done); + server = http.createServer(function (req, res) { + res.setHeader('Content-Type', 'application/json'); + res.setHeader('Content-Encoding', 'gzip'); + res.end(zipped); + }).listen(4444, function () { + axios.get('http://localhost:4444/').then(function (res) { + assert.deepEqual(res.data, data); + done(); + }).catch(done); + }); }); }); - }); - it('should support gunzip error handling', function (done) { - server = http.createServer(function (req, res) { + 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'); - }).listen(4444, function () { - axios.get('http://localhost:4444/').catch(function (error) { - done(); - }).catch(done); }); + + 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'; + it('should support disabling automatic decompression of response data', function(done) { + var data = 'Test data'; + + zlib.gzip(data, function(err, zipped) { + server = http.createServer(function(req, res) { + res.setHeader('Content-Type', 'text/html;charset=utf-8'); + res.setHeader('Content-Encoding', 'gzip'); + res.end(zipped); + }).listen(4444, function() { + axios.get('http://localhost:4444/', { + decompress: false, + responseType: 'arraybuffer' + + }).then(function(res) { + assert.equal(res.data.toString('base64'), zipped.toString('base64')); + done(); + }).catch(done); + }); + }); + }); + + it('should properly handle empty responses without Z_BUF_ERROR throwing', async () => { + this.timeout(10000); - zlib.gzip(data, function(err, zipped) { - server = http.createServer(function(req, res) { - res.setHeader('Content-Type', 'text/html;charset=utf-8'); + server = await startHTTPServer((req, res) => { res.setHeader('Content-Encoding', 'gzip'); - res.end(zipped); - }).listen(4444, function() { - axios.get('http://localhost:4444/', { - decompress: false, - responseType: 'arraybuffer' - - }).then(function(res) { - assert.equal(res.data.toString('base64'), zipped.toString('base64')); - done(); - }).catch(done); + res.end(); }); + + await axios.get(LOCAL_SERVER_URL); }); }); @@ -1887,7 +1905,6 @@ describe('supports http with nodejs', function () { }); }); - describe('request aborting', function() { it('should be able to abort the response stream', async function () { server = await startHTTPServer({