From 91b9b57f5fffb85b8ba994d86a18504b74e7838a Mon Sep 17 00:00:00 2001 From: uzlopak Date: Sat, 27 Jan 2024 07:22:32 +0100 Subject: [PATCH] fix: support deflate raw responses --- lib/fetch/index.js | 36 ++++++++++++++++++++++++++++++++---- test/node-fetch/main.js | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/fetch/index.js b/lib/fetch/index.js index 75939e18599..e835ed21c71 100644 --- a/lib/fetch/index.js +++ b/lib/fetch/index.js @@ -57,7 +57,7 @@ const { } = require('./constants') const { kHeadersList, kConstruct } = require('../core/symbols') const EE = require('events') -const { Readable, pipeline } = require('stream') +const { Readable, pipeline, Transform } = require('stream') const { addAbortListener, isErrored, isReadable, nodeMajor, nodeMinor, bufferToLowerCasedHeaderName } = require('../core/util') const { dataURLProcessor, serializeAMimeType, parseMIMEType } = require('./dataURL') const { getGlobalDispatcher } = require('../global') @@ -1096,7 +1096,7 @@ function fetchFinale (fetchParams, response) { // 3. Set up transformStream with transformAlgorithm set to identityTransformAlgorithm and flushAlgorithm // set to processResponseEndOfBody. const transformStream = new TransformStream({ - start () {}, + start () { }, transform (chunk, controller) { controller.enqueue(chunk) }, @@ -2186,7 +2186,35 @@ async function httpNetworkFetch ( finishFlush: zlib.constants.Z_SYNC_FLUSH })) } else if (coding === 'deflate') { - decoders.push(zlib.createInflate()) + // Instantiate a Stream, which pipes the response to zlib.createInflate() + // or zlib.createInflateRaw() depending on the first byte of the Buffer. + // If the lower byte of the first byte is 0x08, then the stream is + // interpreted as a zlib stream, otherwise it's interpreted as a + // raw deflate stream. + const deflateDecoder = new Transform({ + transform (chunk, encoding, callback) { + if (!this._inflateStream) { + this._inflateStream = (chunk[0] & 0x0F) === 0x08 + ? zlib.createInflate() + : zlib.createInflateRaw() + + this._inflateStream.on('data', this.push.bind(this)) + this._inflateStream.on('end', callback) + this._inflateStream.on('error', callback) + } + + this._inflateStream.write(chunk, encoding, callback) + }, + final (callback) { + if (this._inflateStream) { + this._inflateStream.end() + this._inflateStream = null + } + callback() + } + }) + + decoders.push(deflateDecoder) } else if (coding === 'br') { decoders.push(zlib.createBrotliDecompress()) } else { @@ -2202,7 +2230,7 @@ async function httpNetworkFetch ( headersList, body: decoders.length ? pipeline(this.body, ...decoders, () => { }) - : this.body.on('error', () => {}) + : this.body.on('error', () => { }) }) return true diff --git a/test/node-fetch/main.js b/test/node-fetch/main.js index d702a2743fc..6c96680e901 100644 --- a/test/node-fetch/main.js +++ b/test/node-fetch/main.js @@ -659,7 +659,7 @@ describe('node-fetch', () => { }) }) - xit('should decompress deflate raw response from old apache server', () => { + it('should decompress deflate raw response from old apache server', () => { const url = `${base}deflate-raw` return fetch(url).then(res => { expect(res.headers.get('content-type')).to.equal('text/plain')