mirror of
https://github.com/tenrok/axios.git
synced 2026-06-23 20:40:40 +03:00
Fixed decompression for responses without Content-Length header (#5306)
* Fixed decompression for responses without `content-length` header; * Add Brotli encoding to the `Accept-Encoding` header only if it is supported. * `content-length` header transformation moved up; Co-authored-by: Jay <jasonsaayman@gmail.com>
This commit is contained in:
+16
-8
@@ -20,6 +20,11 @@ import AxiosHeaders from '../core/AxiosHeaders.js';
|
|||||||
import AxiosTransformStream from '../helpers/AxiosTransformStream.js';
|
import AxiosTransformStream from '../helpers/AxiosTransformStream.js';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
|
||||||
|
const zlibOptions = {
|
||||||
|
flush: zlib.constants.Z_SYNC_FLUSH,
|
||||||
|
finishFlush: zlib.constants.Z_SYNC_FLUSH
|
||||||
|
}
|
||||||
|
|
||||||
const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
|
const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
|
||||||
|
|
||||||
const {http: httpFollow, https: httpsFollow} = followRedirects;
|
const {http: httpFollow, https: httpsFollow} = followRedirects;
|
||||||
@@ -262,7 +267,7 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const contentLength = +headers.getContentLength();
|
const contentLength = utils.toFiniteNumber(headers.getContentLength());
|
||||||
|
|
||||||
if (utils.isArray(maxRate)) {
|
if (utils.isArray(maxRate)) {
|
||||||
maxUploadRate = maxRate[0];
|
maxUploadRate = maxRate[0];
|
||||||
@@ -277,7 +282,7 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data = stream.pipeline([data, new AxiosTransformStream({
|
data = stream.pipeline([data, new AxiosTransformStream({
|
||||||
length: utils.toFiniteNumber(contentLength),
|
length: contentLength,
|
||||||
maxRate: utils.toFiniteNumber(maxUploadRate)
|
maxRate: utils.toFiniteNumber(maxUploadRate)
|
||||||
})], utils.noop);
|
})], utils.noop);
|
||||||
|
|
||||||
@@ -320,7 +325,10 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
|||||||
return reject(customErr);
|
return reject(customErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
headers.set('Accept-Encoding', 'gzip, deflate, br', false);
|
headers.set(
|
||||||
|
'Accept-Encoding',
|
||||||
|
'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''), false
|
||||||
|
);
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
path,
|
path,
|
||||||
@@ -392,17 +400,17 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
|||||||
streams.push(transformStream);
|
streams.push(transformStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// uncompress the response body transparently if required
|
// decompress the response body transparently if required
|
||||||
let responseStream = res;
|
let responseStream = res;
|
||||||
|
|
||||||
// return the last request in case of redirects
|
// return the last request in case of redirects
|
||||||
const lastRequest = res.req || req;
|
const lastRequest = res.req || req;
|
||||||
|
|
||||||
// if decompress disabled we should not decompress
|
// if decompress disabled we should not decompress
|
||||||
if (config.decompress !== false) {
|
if (config.decompress !== false && res.headers['content-encoding']) {
|
||||||
// if no content, but headers still say that it is encoded,
|
// if no content, but headers still say that it is encoded,
|
||||||
// remove the header not confuse downstream operations
|
// remove the header not confuse downstream operations
|
||||||
if ((!responseLength || res.statusCode === 204) && res.headers['content-encoding']) {
|
if (method === 'HEAD' || res.statusCode === 204) {
|
||||||
delete res.headers['content-encoding'];
|
delete res.headers['content-encoding'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,14 +420,14 @@ export default isHttpAdapterSupported && function httpAdapter(config) {
|
|||||||
case 'compress':
|
case 'compress':
|
||||||
case 'deflate':
|
case 'deflate':
|
||||||
// add the unzipper to the body stream processing pipeline
|
// add the unzipper to the body stream processing pipeline
|
||||||
streams.push(zlib.createUnzip());
|
streams.push(zlib.createUnzip(zlibOptions));
|
||||||
|
|
||||||
// remove the content-encoding in order to not confuse downstream operations
|
// remove the content-encoding in order to not confuse downstream operations
|
||||||
delete res.headers['content-encoding'];
|
delete res.headers['content-encoding'];
|
||||||
break;
|
break;
|
||||||
case 'br':
|
case 'br':
|
||||||
if (isBrotliSupported) {
|
if (isBrotliSupported) {
|
||||||
streams.push(zlib.createBrotliDecompress());
|
streams.push(zlib.createBrotliDecompress(zlibOptions));
|
||||||
delete res.headers['content-encoding'];
|
delete res.headers['content-encoding'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ function setTimeoutAsync(ms) {
|
|||||||
|
|
||||||
const pipelineAsync = util.promisify(stream.pipeline);
|
const pipelineAsync = util.promisify(stream.pipeline);
|
||||||
const finishedAsync = util.promisify(stream.finished);
|
const finishedAsync = util.promisify(stream.finished);
|
||||||
|
const gzip = util.promisify(zlib.gzip);
|
||||||
|
|
||||||
function toleranceRange(positive, negative) {
|
function toleranceRange(positive, negative) {
|
||||||
const p = (1 + 1 / positive);
|
const p = (1 + 1 / positive);
|
||||||
@@ -497,6 +498,23 @@ describe('supports http with nodejs', function () {
|
|||||||
|
|
||||||
await axios.get(LOCAL_SERVER_URL);
|
await axios.get(LOCAL_SERVER_URL);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not fail if response content-length header is missing', async () => {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
const str = 'zipped';
|
||||||
|
const zipped = await gzip(str);
|
||||||
|
|
||||||
|
server = await startHTTPServer((req, res) => {
|
||||||
|
res.setHeader('Content-Encoding', 'gzip');
|
||||||
|
res.removeHeader('Content-Length');
|
||||||
|
res.end(zipped);
|
||||||
|
});
|
||||||
|
|
||||||
|
const {data} = await axios.get(LOCAL_SERVER_URL);
|
||||||
|
|
||||||
|
assert.strictEqual(data, str);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support UTF8', function (done) {
|
it('should support UTF8', function (done) {
|
||||||
@@ -1411,7 +1429,6 @@ describe('supports http with nodejs', function () {
|
|||||||
|
|
||||||
it('should omit a user-agent if one is explicitly disclaimed', function (done) {
|
it('should omit a user-agent if one is explicitly disclaimed', function (done) {
|
||||||
server = http.createServer(function (req, res) {
|
server = http.createServer(function (req, res) {
|
||||||
console.log(req.headers);
|
|
||||||
assert.equal("user-agent" in req.headers, false);
|
assert.equal("user-agent" in req.headers, false);
|
||||||
assert.equal("User-Agent" in req.headers, false);
|
assert.equal("User-Agent" in req.headers, false);
|
||||||
res.end();
|
res.end();
|
||||||
|
|||||||
Reference in New Issue
Block a user