axios 0.27.2 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of axios might be problematic. Click here for more details.

Files changed (75) hide show
  1. package/CHANGELOG.md +232 -920
  2. package/LICENSE +4 -16
  3. package/README.md +386 -103
  4. package/SECURITY.md +4 -3
  5. package/UPGRADE_GUIDE.md +1 -166
  6. package/bin/ssl_hotfix.js +22 -0
  7. package/dist/axios.js +2430 -2367
  8. package/dist/axios.js.map +1 -0
  9. package/dist/axios.min.js +2 -3
  10. package/dist/axios.min.js.map +1 -0
  11. package/dist/esm/axios.js +2950 -0
  12. package/dist/esm/axios.js.map +1 -0
  13. package/dist/esm/axios.min.js +2 -0
  14. package/dist/esm/axios.min.js.map +1 -0
  15. package/dist/node/axios.cjs +3764 -0
  16. package/dist/node/axios.cjs.map +1 -0
  17. package/gulpfile.js +88 -0
  18. package/index.d.ts +299 -70
  19. package/index.js +32 -1
  20. package/karma.conf.cjs +250 -0
  21. package/lib/adapters/http.js +378 -211
  22. package/lib/adapters/index.js +33 -0
  23. package/lib/adapters/xhr.js +81 -57
  24. package/lib/axios.js +34 -22
  25. package/lib/cancel/CancelToken.js +91 -89
  26. package/lib/cancel/CanceledError.js +9 -6
  27. package/lib/cancel/isCancel.js +2 -2
  28. package/lib/core/Axios.js +133 -98
  29. package/lib/core/AxiosError.js +22 -8
  30. package/lib/core/AxiosHeaders.js +268 -0
  31. package/lib/core/InterceptorManager.js +62 -45
  32. package/lib/core/buildFullPath.js +5 -4
  33. package/lib/core/dispatchRequest.js +21 -32
  34. package/lib/core/mergeConfig.js +8 -7
  35. package/lib/core/settle.js +6 -4
  36. package/lib/core/transformData.js +15 -9
  37. package/lib/defaults/index.js +77 -38
  38. package/lib/defaults/transitional.js +1 -1
  39. package/lib/env/classes/FormData.js +2 -0
  40. package/lib/env/data.js +1 -3
  41. package/lib/helpers/AxiosTransformStream.js +191 -0
  42. package/lib/helpers/AxiosURLSearchParams.js +58 -0
  43. package/lib/helpers/bind.js +3 -7
  44. package/lib/helpers/buildURL.js +26 -33
  45. package/lib/helpers/combineURLs.js +3 -2
  46. package/lib/helpers/cookies.js +43 -44
  47. package/lib/helpers/deprecatedMethod.js +4 -2
  48. package/lib/helpers/formDataToJSON.js +92 -0
  49. package/lib/helpers/fromDataURI.js +53 -0
  50. package/lib/helpers/isAbsoluteURL.js +3 -2
  51. package/lib/helpers/isAxiosError.js +4 -3
  52. package/lib/helpers/isURLSameOrigin.js +44 -45
  53. package/lib/helpers/null.js +1 -1
  54. package/lib/helpers/parseHeaders.js +24 -22
  55. package/lib/helpers/parseProtocol.js +3 -3
  56. package/lib/helpers/speedometer.js +55 -0
  57. package/lib/helpers/spread.js +3 -2
  58. package/lib/helpers/throttle.js +33 -0
  59. package/lib/helpers/toFormData.js +193 -36
  60. package/lib/helpers/toURLEncodedForm.js +18 -0
  61. package/lib/helpers/validator.js +20 -15
  62. package/lib/platform/browser/classes/FormData.js +3 -0
  63. package/lib/platform/browser/classes/URLSearchParams.js +4 -0
  64. package/lib/platform/browser/index.js +43 -0
  65. package/lib/platform/index.js +3 -0
  66. package/lib/platform/node/classes/FormData.js +3 -0
  67. package/lib/platform/node/classes/URLSearchParams.js +4 -0
  68. package/lib/platform/node/index.js +12 -0
  69. package/lib/utils.js +321 -178
  70. package/package.json +70 -23
  71. package/rollup.config.js +90 -0
  72. package/dist/axios.map +0 -1
  73. package/dist/axios.min.map +0 -1
  74. package/lib/defaults/env/FormData.js +0 -2
  75. package/lib/helpers/normalizeHeaderName.js +0 -12
@@ -1,96 +1,238 @@
1
1
  'use strict';
2
2
 
3
- var utils = require('./../utils');
4
- var settle = require('./../core/settle');
5
- var buildFullPath = require('../core/buildFullPath');
6
- var buildURL = require('./../helpers/buildURL');
7
- var http = require('http');
8
- var https = require('https');
9
- var httpFollow = require('follow-redirects').http;
10
- var httpsFollow = require('follow-redirects').https;
11
- var url = require('url');
12
- var zlib = require('zlib');
13
- var VERSION = require('./../env/data').version;
14
- var transitionalDefaults = require('../defaults/transitional');
15
- var AxiosError = require('../core/AxiosError');
16
- var CanceledError = require('../cancel/CanceledError');
17
-
18
- var isHttps = /https:?/;
19
-
20
- var supportedProtocols = [ 'http:', 'https:', 'file:' ];
3
+ import utils from './../utils.js';
4
+ import settle from './../core/settle.js';
5
+ import buildFullPath from '../core/buildFullPath.js';
6
+ import buildURL from './../helpers/buildURL.js';
7
+ import {getProxyForUrl} from 'proxy-from-env';
8
+ import http from 'http';
9
+ import https from 'https';
10
+ import followRedirects from 'follow-redirects';
11
+ import zlib from 'zlib';
12
+ import {VERSION} from '../env/data.js';
13
+ import transitionalDefaults from '../defaults/transitional.js';
14
+ import AxiosError from '../core/AxiosError.js';
15
+ import CanceledError from '../cancel/CanceledError.js';
16
+ import platform from '../platform/index.js';
17
+ import fromDataURI from '../helpers/fromDataURI.js';
18
+ import stream from 'stream';
19
+ import AxiosHeaders from '../core/AxiosHeaders.js';
20
+ import AxiosTransformStream from '../helpers/AxiosTransformStream.js';
21
+ import EventEmitter from 'events';
22
+
23
+ const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
24
+
25
+ const {http: httpFollow, https: httpsFollow} = followRedirects;
26
+
27
+ const isHttps = /https:?/;
28
+
29
+ const supportedProtocols = platform.protocols.map(protocol => {
30
+ return protocol + ':';
31
+ });
21
32
 
22
33
  /**
34
+ * If the proxy or config beforeRedirects functions are defined, call them with the options
35
+ * object.
36
+ *
37
+ * @param {Object<string, any>} options - The options object that was passed to the request.
38
+ *
39
+ * @returns {Object<string, any>}
40
+ */
41
+ function dispatchBeforeRedirect(options) {
42
+ if (options.beforeRedirects.proxy) {
43
+ options.beforeRedirects.proxy(options);
44
+ }
45
+ if (options.beforeRedirects.config) {
46
+ options.beforeRedirects.config(options);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * If the proxy or config afterRedirects functions are defined, call them with the options
23
52
  *
24
53
  * @param {http.ClientRequestArgs} options
25
- * @param {AxiosProxyConfig} proxy
54
+ * @param {AxiosProxyConfig} configProxy configuration from Axios options object
26
55
  * @param {string} location
56
+ *
57
+ * @returns {http.ClientRequestArgs}
27
58
  */
28
- function setProxy(options, proxy, location) {
29
- options.hostname = proxy.host;
30
- options.host = proxy.host;
31
- options.port = proxy.port;
32
- options.path = location;
33
-
34
- // Basic proxy authorization
35
- if (proxy.auth) {
36
- var base64 = Buffer.from(proxy.auth.username + ':' + proxy.auth.password, 'utf8').toString('base64');
37
- options.headers['Proxy-Authorization'] = 'Basic ' + base64;
59
+ function setProxy(options, configProxy, location) {
60
+ let proxy = configProxy;
61
+ if (!proxy && proxy !== false) {
62
+ const proxyUrl = getProxyForUrl(location);
63
+ if (proxyUrl) {
64
+ proxy = new URL(proxyUrl);
65
+ }
38
66
  }
67
+ if (proxy) {
68
+ // Basic proxy authorization
69
+ if (proxy.username) {
70
+ proxy.auth = (proxy.username || '') + ':' + (proxy.password || '');
71
+ }
72
+
73
+ if (proxy.auth) {
74
+ // Support proxy auth object form
75
+ if (proxy.auth.username || proxy.auth.password) {
76
+ proxy.auth = (proxy.auth.username || '') + ':' + (proxy.auth.password || '');
77
+ }
78
+ const base64 = Buffer
79
+ .from(proxy.auth, 'utf8')
80
+ .toString('base64');
81
+ options.headers['Proxy-Authorization'] = 'Basic ' + base64;
82
+ }
39
83
 
40
- // If a proxy is used, any redirects must also pass through the proxy
41
- options.beforeRedirect = function beforeRedirect(redirection) {
42
- redirection.headers.host = redirection.host;
43
- setProxy(redirection, proxy, redirection.href);
84
+ options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
85
+ const proxyHost = proxy.hostname || proxy.host;
86
+ options.hostname = proxyHost;
87
+ // Replace 'host' since options is not a URL object
88
+ options.host = proxyHost;
89
+ options.port = proxy.port;
90
+ options.path = location;
91
+ if (proxy.protocol) {
92
+ options.protocol = proxy.protocol.includes(':') ? proxy.protocol : `${proxy.protocol}:`;
93
+ }
94
+ }
95
+
96
+ options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) {
97
+ // Configure proxy for redirected request, passing the original config proxy to apply
98
+ // the exact same logic as if the redirected request was performed by axios directly.
99
+ setProxy(redirectOptions, configProxy, redirectOptions.href);
44
100
  };
45
101
  }
46
102
 
47
103
  /*eslint consistent-return:0*/
48
- module.exports = function httpAdapter(config) {
104
+ export default function httpAdapter(config) {
49
105
  return new Promise(function dispatchHttpRequest(resolvePromise, rejectPromise) {
50
- var onCanceled;
51
- function done() {
106
+ let data = config.data;
107
+ const responseType = config.responseType;
108
+ const responseEncoding = config.responseEncoding;
109
+ const method = config.method.toUpperCase();
110
+ let isFinished;
111
+ let isDone;
112
+ let rejected = false;
113
+ let req;
114
+
115
+ // temporary internal emitter until the AxiosRequest class will be implemented
116
+ const emitter = new EventEmitter();
117
+
118
+ function onFinished() {
119
+ if (isFinished) return;
120
+ isFinished = true;
121
+
52
122
  if (config.cancelToken) {
53
- config.cancelToken.unsubscribe(onCanceled);
123
+ config.cancelToken.unsubscribe(abort);
54
124
  }
55
125
 
56
126
  if (config.signal) {
57
- config.signal.removeEventListener('abort', onCanceled);
127
+ config.signal.removeEventListener('abort', abort);
58
128
  }
129
+
130
+ emitter.removeAllListeners();
59
131
  }
60
- var resolve = function resolve(value) {
61
- done();
62
- resolvePromise(value);
132
+
133
+ function done(value, isRejected) {
134
+ if (isDone) return;
135
+
136
+ isDone = true;
137
+
138
+ if (isRejected) {
139
+ rejected = true;
140
+ onFinished();
141
+ }
142
+
143
+ isRejected ? rejectPromise(value) : resolvePromise(value);
144
+ }
145
+
146
+ const resolve = function resolve(value) {
147
+ done(value);
63
148
  };
64
- var rejected = false;
65
- var reject = function reject(value) {
66
- done();
67
- rejected = true;
68
- rejectPromise(value);
149
+
150
+ const reject = function reject(value) {
151
+ done(value, true);
69
152
  };
70
- var data = config.data;
71
- var headers = config.headers;
72
- var headerNames = {};
73
153
 
74
- Object.keys(headers).forEach(function storeLowerName(name) {
75
- headerNames[name.toLowerCase()] = name;
76
- });
154
+ function abort(reason) {
155
+ emitter.emit('abort', !reason || reason.type ? new CanceledError(null, config, req) : reason);
156
+ }
77
157
 
78
- // Set User-Agent (required by some servers)
79
- // See https://github.com/axios/axios/issues/69
80
- if ('user-agent' in headerNames) {
81
- // User-Agent is specified; handle case where no UA header is desired
82
- if (!headers[headerNames['user-agent']]) {
83
- delete headers[headerNames['user-agent']];
158
+ emitter.once('abort', reject);
159
+
160
+ if (config.cancelToken || config.signal) {
161
+ config.cancelToken && config.cancelToken.subscribe(abort);
162
+ if (config.signal) {
163
+ config.signal.aborted ? abort() : config.signal.addEventListener('abort', abort);
84
164
  }
85
- // Otherwise, use specified value
86
- } else {
87
- // Only set header if it hasn't been set in config
88
- headers['User-Agent'] = 'axios/' + VERSION;
89
165
  }
90
166
 
167
+ // Parse url
168
+ const fullPath = buildFullPath(config.baseURL, config.url);
169
+ const parsed = new URL(fullPath);
170
+ const protocol = parsed.protocol || supportedProtocols[0];
171
+
172
+ if (protocol === 'data:') {
173
+ let convertedData;
174
+
175
+ if (method !== 'GET') {
176
+ return settle(resolve, reject, {
177
+ status: 405,
178
+ statusText: 'method not allowed',
179
+ headers: {},
180
+ config
181
+ });
182
+ }
183
+
184
+ try {
185
+ convertedData = fromDataURI(config.url, responseType === 'blob', {
186
+ Blob: config.env && config.env.Blob
187
+ });
188
+ } catch (err) {
189
+ throw AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config);
190
+ }
191
+
192
+ if (responseType === 'text') {
193
+ convertedData = convertedData.toString(responseEncoding);
194
+
195
+ if (!responseEncoding || responseEncoding === 'utf8') {
196
+ data = utils.stripBOM(convertedData);
197
+ }
198
+ } else if (responseType === 'stream') {
199
+ convertedData = stream.Readable.from(convertedData);
200
+ }
201
+
202
+ return settle(resolve, reject, {
203
+ data: convertedData,
204
+ status: 200,
205
+ statusText: 'OK',
206
+ headers: {},
207
+ config
208
+ });
209
+ }
210
+
211
+ if (supportedProtocols.indexOf(protocol) === -1) {
212
+ return reject(new AxiosError(
213
+ 'Unsupported protocol ' + protocol,
214
+ AxiosError.ERR_BAD_REQUEST,
215
+ config
216
+ ));
217
+ }
218
+
219
+ const headers = AxiosHeaders.from(config.headers).normalize();
220
+
221
+ // Set User-Agent (required by some servers)
222
+ // See https://github.com/axios/axios/issues/69
223
+ // User-Agent is specified; handle case where no UA header is desired
224
+ // Only set header if it hasn't been set in config
225
+ headers.set('User-Agent', 'axios/' + VERSION, false);
226
+
227
+ const onDownloadProgress = config.onDownloadProgress;
228
+ const onUploadProgress = config.onUploadProgress;
229
+ const maxRate = config.maxRate;
230
+ let maxUploadRate = undefined;
231
+ let maxDownloadRate = undefined;
232
+
91
233
  // support for https://www.npmjs.com/package/form-data api
92
234
  if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) {
93
- Object.assign(headers, data.getHeaders());
235
+ headers.set(data.getHeaders());
94
236
  } else if (data && !utils.isStream(data)) {
95
237
  if (Buffer.isBuffer(data)) {
96
238
  // Nothing to do...
@@ -106,6 +248,9 @@ module.exports = function httpAdapter(config) {
106
248
  ));
107
249
  }
108
250
 
251
+ // Add Content-Length header if data exists
252
+ headers.set('Content-Length', data.length, false);
253
+
109
254
  if (config.maxBodyLength > -1 && data.length > config.maxBodyLength) {
110
255
  return reject(new AxiosError(
111
256
  'Request body larger than maxBodyLength limit',
@@ -113,65 +258,77 @@ module.exports = function httpAdapter(config) {
113
258
  config
114
259
  ));
115
260
  }
261
+ }
116
262
 
117
- // Add Content-Length header if data exists
118
- if (!headerNames['content-length']) {
119
- headers['Content-Length'] = data.length;
263
+ const contentLength = +headers.getContentLength();
264
+
265
+ if (utils.isArray(maxRate)) {
266
+ maxUploadRate = maxRate[0];
267
+ maxDownloadRate = maxRate[1];
268
+ } else {
269
+ maxUploadRate = maxDownloadRate = maxRate;
270
+ }
271
+
272
+ if (data && (onUploadProgress || maxUploadRate)) {
273
+ if (!utils.isStream(data)) {
274
+ data = stream.Readable.from(data, {objectMode: false});
120
275
  }
276
+
277
+ data = stream.pipeline([data, new AxiosTransformStream({
278
+ length: utils.toFiniteNumber(contentLength),
279
+ maxRate: utils.toFiniteNumber(maxUploadRate)
280
+ })], utils.noop);
281
+
282
+ onUploadProgress && data.on('progress', progress => {
283
+ onUploadProgress(Object.assign(progress, {
284
+ upload: true
285
+ }));
286
+ });
121
287
  }
122
288
 
123
289
  // HTTP basic authentication
124
- var auth = undefined;
290
+ let auth = undefined;
125
291
  if (config.auth) {
126
- var username = config.auth.username || '';
127
- var password = config.auth.password || '';
292
+ const username = config.auth.username || '';
293
+ const password = config.auth.password || '';
128
294
  auth = username + ':' + password;
129
295
  }
130
296
 
131
- // Parse url
132
- var fullPath = buildFullPath(config.baseURL, config.url);
133
- var parsed = url.parse(fullPath);
134
- var protocol = parsed.protocol || supportedProtocols[0];
135
-
136
- if (supportedProtocols.indexOf(protocol) === -1) {
137
- return reject(new AxiosError(
138
- 'Unsupported protocol ' + protocol,
139
- AxiosError.ERR_BAD_REQUEST,
140
- config
141
- ));
142
- }
143
-
144
- if (!auth && parsed.auth) {
145
- var urlAuth = parsed.auth.split(':');
146
- var urlUsername = urlAuth[0] || '';
147
- var urlPassword = urlAuth[1] || '';
297
+ if (!auth && parsed.username) {
298
+ const urlUsername = parsed.username;
299
+ const urlPassword = parsed.password;
148
300
  auth = urlUsername + ':' + urlPassword;
149
301
  }
150
302
 
151
- if (auth && headerNames.authorization) {
152
- delete headers[headerNames.authorization];
153
- }
303
+ auth && headers.delete('authorization');
154
304
 
155
- var isHttpsRequest = isHttps.test(protocol);
156
- var agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
305
+ let path;
157
306
 
158
307
  try {
159
- buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, '');
308
+ path = buildURL(
309
+ parsed.pathname + parsed.search,
310
+ config.params,
311
+ config.paramsSerializer
312
+ ).replace(/^\?/, '');
160
313
  } catch (err) {
161
- var customErr = new Error(err.message);
314
+ const customErr = new Error(err.message);
162
315
  customErr.config = config;
163
316
  customErr.url = config.url;
164
317
  customErr.exists = true;
165
- reject(customErr);
318
+ return reject(customErr);
166
319
  }
167
320
 
168
- var options = {
169
- path: buildURL(parsed.path, config.params, config.paramsSerializer).replace(/^\?/, ''),
170
- method: config.method.toUpperCase(),
171
- headers: headers,
172
- agent: agent,
321
+ headers.set('Accept-Encoding', 'gzip, deflate, br', false);
322
+
323
+ const options = {
324
+ path,
325
+ method: method,
326
+ headers: headers.toJSON(),
173
327
  agents: { http: config.httpAgent, https: config.httpsAgent },
174
- auth: auth
328
+ auth,
329
+ protocol,
330
+ beforeRedirect: dispatchBeforeRedirect,
331
+ beforeRedirects: {}
175
332
  };
176
333
 
177
334
  if (config.socketPath) {
@@ -179,79 +336,31 @@ module.exports = function httpAdapter(config) {
179
336
  } else {
180
337
  options.hostname = parsed.hostname;
181
338
  options.port = parsed.port;
339
+ setProxy(options, config.proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path);
182
340
  }
183
341
 
184
- var proxy = config.proxy;
185
- if (!proxy && proxy !== false) {
186
- var proxyEnv = protocol.slice(0, -1) + '_proxy';
187
- var proxyUrl = process.env[proxyEnv] || process.env[proxyEnv.toUpperCase()];
188
- if (proxyUrl) {
189
- var parsedProxyUrl = url.parse(proxyUrl);
190
- var noProxyEnv = process.env.no_proxy || process.env.NO_PROXY;
191
- var shouldProxy = true;
192
-
193
- if (noProxyEnv) {
194
- var noProxy = noProxyEnv.split(',').map(function trim(s) {
195
- return s.trim();
196
- });
197
-
198
- shouldProxy = !noProxy.some(function proxyMatch(proxyElement) {
199
- if (!proxyElement) {
200
- return false;
201
- }
202
- if (proxyElement === '*') {
203
- return true;
204
- }
205
- if (proxyElement[0] === '.' &&
206
- parsed.hostname.substr(parsed.hostname.length - proxyElement.length) === proxyElement) {
207
- return true;
208
- }
209
-
210
- return parsed.hostname === proxyElement;
211
- });
212
- }
213
-
214
- if (shouldProxy) {
215
- proxy = {
216
- host: parsedProxyUrl.hostname,
217
- port: parsedProxyUrl.port,
218
- protocol: parsedProxyUrl.protocol
219
- };
220
-
221
- if (parsedProxyUrl.auth) {
222
- var proxyUrlAuth = parsedProxyUrl.auth.split(':');
223
- proxy.auth = {
224
- username: proxyUrlAuth[0],
225
- password: proxyUrlAuth[1]
226
- };
227
- }
228
- }
229
- }
230
- }
231
-
232
- if (proxy) {
233
- options.headers.host = parsed.hostname + (parsed.port ? ':' + parsed.port : '');
234
- setProxy(options, proxy, protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path);
235
- }
236
-
237
- var transport;
238
- var isHttpsProxy = isHttpsRequest && (proxy ? isHttps.test(proxy.protocol) : true);
342
+ let transport;
343
+ const isHttpsRequest = isHttps.test(options.protocol);
344
+ options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
239
345
  if (config.transport) {
240
346
  transport = config.transport;
241
347
  } else if (config.maxRedirects === 0) {
242
- transport = isHttpsProxy ? https : http;
348
+ transport = isHttpsRequest ? https : http;
243
349
  } else {
244
350
  if (config.maxRedirects) {
245
351
  options.maxRedirects = config.maxRedirects;
246
352
  }
247
353
  if (config.beforeRedirect) {
248
- options.beforeRedirect = config.beforeRedirect;
354
+ options.beforeRedirects.config = config.beforeRedirect;
249
355
  }
250
- transport = isHttpsProxy ? httpsFollow : httpFollow;
356
+ transport = isHttpsRequest ? httpsFollow : httpFollow;
251
357
  }
252
358
 
253
359
  if (config.maxBodyLength > -1) {
254
360
  options.maxBodyLength = config.maxBodyLength;
361
+ } else {
362
+ // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
363
+ options.maxBodyLength = Infinity;
255
364
  }
256
365
 
257
366
  if (config.insecureHTTPParser) {
@@ -259,84 +368,123 @@ module.exports = function httpAdapter(config) {
259
368
  }
260
369
 
261
370
  // Create the request
262
- var req = transport.request(options, function handleResponse(res) {
263
- if (req.aborted) return;
371
+ req = transport.request(options, function handleResponse(res) {
372
+ if (req.destroyed) return;
373
+
374
+ const streams = [res];
264
375
 
265
376
  // uncompress the response body transparently if required
266
- var stream = res;
377
+ let responseStream = res;
267
378
 
268
379
  // return the last request in case of redirects
269
- var lastRequest = res.req || req;
380
+ const lastRequest = res.req || req;
270
381
 
382
+ // if decompress disabled we should not decompress
383
+ if (config.decompress !== false) {
384
+ // if no content, but headers still say that it is encoded,
385
+ // remove the header not confuse downstream operations
386
+ if (data && data.length === 0 && res.headers['content-encoding']) {
387
+ delete res.headers['content-encoding'];
388
+ }
271
389
 
272
- // if no content, is HEAD request or decompress disabled we should not decompress
273
- if (res.statusCode !== 204 && lastRequest.method !== 'HEAD' && config.decompress !== false) {
274
390
  switch (res.headers['content-encoding']) {
275
391
  /*eslint default-case:0*/
276
392
  case 'gzip':
277
393
  case 'compress':
278
394
  case 'deflate':
279
- // add the unzipper to the body stream processing pipeline
280
- stream = stream.pipe(zlib.createUnzip());
395
+ // add the unzipper to the body stream processing pipeline
396
+ streams.push(zlib.createUnzip());
281
397
 
282
398
  // remove the content-encoding in order to not confuse downstream operations
283
399
  delete res.headers['content-encoding'];
284
400
  break;
401
+ case 'br':
402
+ if (isBrotliSupported) {
403
+ streams.push(zlib.createBrotliDecompress());
404
+ delete res.headers['content-encoding'];
405
+ }
285
406
  }
286
407
  }
287
408
 
288
- var response = {
409
+ if (onDownloadProgress) {
410
+ const responseLength = +res.headers['content-length'];
411
+
412
+ const transformStream = new AxiosTransformStream({
413
+ length: utils.toFiniteNumber(responseLength),
414
+ maxRate: utils.toFiniteNumber(maxDownloadRate)
415
+ });
416
+
417
+ onDownloadProgress && transformStream.on('progress', progress => {
418
+ onDownloadProgress(Object.assign(progress, {
419
+ download: true
420
+ }));
421
+ });
422
+
423
+ streams.push(transformStream);
424
+ }
425
+
426
+ responseStream = streams.length > 1 ? stream.pipeline(streams, utils.noop) : streams[0];
427
+
428
+ const offListeners = stream.finished(responseStream, () => {
429
+ offListeners();
430
+ onFinished();
431
+ });
432
+
433
+ const response = {
289
434
  status: res.statusCode,
290
435
  statusText: res.statusMessage,
291
- headers: res.headers,
292
- config: config,
436
+ headers: new AxiosHeaders(res.headers),
437
+ config,
293
438
  request: lastRequest
294
439
  };
295
440
 
296
- if (config.responseType === 'stream') {
297
- response.data = stream;
441
+ if (responseType === 'stream') {
442
+ response.data = responseStream;
298
443
  settle(resolve, reject, response);
299
444
  } else {
300
- var responseBuffer = [];
301
- var totalResponseBytes = 0;
302
- stream.on('data', function handleStreamData(chunk) {
445
+ const responseBuffer = [];
446
+ let totalResponseBytes = 0;
447
+
448
+ responseStream.on('data', function handleStreamData(chunk) {
303
449
  responseBuffer.push(chunk);
304
450
  totalResponseBytes += chunk.length;
305
451
 
306
452
  // make sure the content length is not over the maxContentLength if specified
307
453
  if (config.maxContentLength > -1 && totalResponseBytes > config.maxContentLength) {
308
- // stream.destoy() emit aborted event before calling reject() on Node.js v16
454
+ // stream.destroy() emit aborted event before calling reject() on Node.js v16
309
455
  rejected = true;
310
- stream.destroy();
456
+ responseStream.destroy();
311
457
  reject(new AxiosError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
312
458
  AxiosError.ERR_BAD_RESPONSE, config, lastRequest));
313
459
  }
314
460
  });
315
461
 
316
- stream.on('aborted', function handlerStreamAborted() {
462
+ responseStream.on('aborted', function handlerStreamAborted() {
317
463
  if (rejected) {
318
464
  return;
319
465
  }
320
- stream.destroy();
321
- reject(new AxiosError(
466
+
467
+ const err = new AxiosError(
322
468
  'maxContentLength size of ' + config.maxContentLength + ' exceeded',
323
469
  AxiosError.ERR_BAD_RESPONSE,
324
470
  config,
325
471
  lastRequest
326
- ));
472
+ );
473
+ responseStream.destroy(err);
474
+ reject(err);
327
475
  });
328
476
 
329
- stream.on('error', function handleStreamError(err) {
330
- if (req.aborted) return;
477
+ responseStream.on('error', function handleStreamError(err) {
478
+ if (req.destroyed) return;
331
479
  reject(AxiosError.from(err, null, config, lastRequest));
332
480
  });
333
481
 
334
- stream.on('end', function handleStreamEnd() {
482
+ responseStream.on('end', function handleStreamEnd() {
335
483
  try {
336
- var responseData = responseBuffer.length === 1 ? responseBuffer[0] : Buffer.concat(responseBuffer);
337
- if (config.responseType !== 'arraybuffer') {
338
- responseData = responseData.toString(config.responseEncoding);
339
- if (!config.responseEncoding || config.responseEncoding === 'utf8') {
484
+ let responseData = responseBuffer.length === 1 ? responseBuffer[0] : Buffer.concat(responseBuffer);
485
+ if (responseType !== 'arraybuffer') {
486
+ responseData = responseData.toString(responseEncoding);
487
+ if (!responseEncoding || responseEncoding === 'utf8') {
340
488
  responseData = utils.stripBOM(responseData);
341
489
  }
342
490
  }
@@ -347,6 +495,18 @@ module.exports = function httpAdapter(config) {
347
495
  settle(resolve, reject, response);
348
496
  });
349
497
  }
498
+
499
+ emitter.once('abort', err => {
500
+ if (!responseStream.destroyed) {
501
+ responseStream.emit('error', err);
502
+ responseStream.destroy();
503
+ }
504
+ });
505
+ });
506
+
507
+ emitter.once('abort', err => {
508
+ reject(err);
509
+ req.destroy(err);
350
510
  });
351
511
 
352
512
  // Handle errors
@@ -365,7 +525,7 @@ module.exports = function httpAdapter(config) {
365
525
  // Handle request timeout
366
526
  if (config.timeout) {
367
527
  // This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types.
368
- var timeout = parseInt(config.timeout, 10);
528
+ const timeout = parseInt(config.timeout, 10);
369
529
 
370
530
  if (isNaN(timeout)) {
371
531
  reject(new AxiosError(
@@ -381,44 +541,51 @@ module.exports = function httpAdapter(config) {
381
541
  // Sometime, the response will be very slow, and does not respond, the connect event will be block by event loop system.
382
542
  // And timer callback will be fired, and abort() will be invoked before connection, then get "socket hang up" and code ECONNRESET.
383
543
  // At this time, if we have a large number of request, nodejs will hang up some socket on background. and the number will up and up.
384
- // And then these socket which be hang up will devoring CPU little by little.
544
+ // And then these socket which be hang up will devouring CPU little by little.
385
545
  // ClientRequest.setTimeout will be fired on the specify milliseconds, and can make sure that abort() will be fired after connect.
386
546
  req.setTimeout(timeout, function handleRequestTimeout() {
387
- req.abort();
388
- var transitional = config.transitional || transitionalDefaults;
547
+ if (isDone) return;
548
+ let timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
549
+ const transitional = config.transitional || transitionalDefaults;
550
+ if (config.timeoutErrorMessage) {
551
+ timeoutErrorMessage = config.timeoutErrorMessage;
552
+ }
389
553
  reject(new AxiosError(
390
- 'timeout of ' + timeout + 'ms exceeded',
554
+ timeoutErrorMessage,
391
555
  transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
392
556
  config,
393
557
  req
394
558
  ));
559
+ abort();
395
560
  });
396
561
  }
397
562
 
398
- if (config.cancelToken || config.signal) {
399
- // Handle cancellation
400
- // eslint-disable-next-line func-names
401
- onCanceled = function(cancel) {
402
- if (req.aborted) return;
403
563
 
404
- req.abort();
405
- reject(!cancel || (cancel && cancel.type) ? new CanceledError() : cancel);
406
- };
564
+ // Send the request
565
+ if (utils.isStream(data)) {
566
+ let ended = false;
567
+ let errored = false;
407
568
 
408
- config.cancelToken && config.cancelToken.subscribe(onCanceled);
409
- if (config.signal) {
410
- config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
411
- }
412
- }
569
+ data.on('end', () => {
570
+ ended = true;
571
+ });
413
572
 
573
+ data.once('error', err => {
574
+ errored = true;
575
+ req.destroy(err);
576
+ });
414
577
 
415
- // Send the request
416
- if (utils.isStream(data)) {
417
- data.on('error', function handleStreamError(err) {
418
- reject(AxiosError.from(err, config, null, req));
419
- }).pipe(req);
578
+ data.on('close', () => {
579
+ if (!ended && !errored) {
580
+ abort(new CanceledError('Request stream has been aborted', config, req));
581
+ }
582
+ });
583
+
584
+ data.pipe(req);
420
585
  } else {
421
586
  req.end(data);
422
587
  }
423
588
  });
424
- };
589
+ }
590
+
591
+ export const __setProxy = setProxy;