axios 1.18.0 → 1.18.1

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.
@@ -89,6 +89,53 @@ const kAxiosInstalledTunnel = Symbol('axios.http.installedTunnel');
89
89
  // so unbounded growth is not a concern in practice.
90
90
  const tunnelingAgentCache = new Map();
91
91
  const tunnelingAgentCacheUser = new WeakMap();
92
+ // Minimum minor versions where Node's HTTP Agent supports native proxyEnv
93
+ // handling. Checking the selected agent below also covers startup modes such
94
+ // as NODE_OPTIONS=--use-env-proxy and --no-use-env-proxy precedence.
95
+ const NODE_NATIVE_ENV_PROXY_SUPPORT = {
96
+ 22: 21,
97
+ 24: 5,
98
+ };
99
+
100
+ function isNodeNativeEnvProxySupported(nodeVersion = process.versions && process.versions.node) {
101
+ if (!nodeVersion) {
102
+ return false;
103
+ }
104
+
105
+ const [major, minor] = nodeVersion.split('.').map((part) => Number(part));
106
+
107
+ if (!Number.isInteger(major) || !Number.isInteger(minor)) {
108
+ return false;
109
+ }
110
+
111
+ if (major > 24) {
112
+ return true;
113
+ }
114
+
115
+ return (
116
+ NODE_NATIVE_ENV_PROXY_SUPPORT[major] != null && minor >= NODE_NATIVE_ENV_PROXY_SUPPORT[major]
117
+ );
118
+ }
119
+
120
+ function isNodeEnvProxyEnabled(agent, nodeVersion = process.versions && process.versions.node) {
121
+ if (!isNodeNativeEnvProxySupported(nodeVersion)) {
122
+ return false;
123
+ }
124
+
125
+ const agentOptions = agent && agent.options;
126
+
127
+ return Boolean(
128
+ agentOptions &&
129
+ utils.hasOwnProp(agentOptions, 'proxyEnv') &&
130
+ agentOptions.proxyEnv != null
131
+ );
132
+ }
133
+
134
+ function getProxyEnvAgent(options, configHttpAgent, configHttpsAgent) {
135
+ return isHttps.test(options.protocol)
136
+ ? (configHttpsAgent || https.globalAgent)
137
+ : (configHttpAgent || http.globalAgent);
138
+ }
92
139
 
93
140
  function getTunnelingAgent(agentOptions, userHttpsAgent) {
94
141
  const key =
@@ -210,9 +257,10 @@ function isSameOriginRedirect(redirectOptions, requestDetails) {
210
257
  *
211
258
  * @returns {http.ClientRequestArgs}
212
259
  */
213
- function setProxy(options, configProxy, location, isRedirect, configHttpsAgent) {
260
+ function setProxy(options, configProxy, location, isRedirect, configHttpsAgent, configHttpAgent) {
214
261
  let proxy = configProxy;
215
- if (!proxy && proxy !== false) {
262
+ const proxyEnvAgent = getProxyEnvAgent(options, configHttpAgent, configHttpsAgent);
263
+ if (!proxy && proxy !== false && !isNodeEnvProxyEnabled(proxyEnvAgent)) {
216
264
  const proxyUrl = getProxyForUrl(location);
217
265
  if (proxyUrl) {
218
266
  if (!shouldBypassProxy(location)) {
@@ -363,7 +411,14 @@ function setProxy(options, configProxy, location, isRedirect, configHttpsAgent)
363
411
  options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) {
364
412
  // Configure proxy for redirected request, passing the original config proxy to apply
365
413
  // the exact same logic as if the redirected request was performed by axios directly.
366
- setProxy(redirectOptions, configProxy, redirectOptions.href, true, configHttpsAgent);
414
+ setProxy(
415
+ redirectOptions,
416
+ configProxy,
417
+ redirectOptions.href,
418
+ true,
419
+ configHttpsAgent,
420
+ configHttpAgent
421
+ );
367
422
  };
368
423
  }
369
424
 
@@ -475,10 +530,12 @@ export default isHttpAdapterSupported &&
475
530
  let httpVersion = own('httpVersion');
476
531
  if (httpVersion === undefined) httpVersion = 1;
477
532
  let http2Options = own('http2Options');
478
- const responseType = own('responseType');
479
- const responseEncoding = own('responseEncoding');
480
533
  const httpAgent = own('httpAgent');
481
534
  const httpsAgent = own('httpsAgent');
535
+ const configProxy = own('proxy');
536
+ const responseType = own('responseType');
537
+ const responseEncoding = own('responseEncoding');
538
+ const socketPath = own('socketPath');
482
539
  const method = own('method').toUpperCase();
483
540
  const maxRedirects = own('maxRedirects');
484
541
  const maxBodyLength = own('maxBodyLength');
@@ -603,7 +660,14 @@ export default isHttpAdapterSupported &&
603
660
 
604
661
  // Parse url
605
662
  const fullPath = buildFullPath(own('baseURL'), own('url'), own('allowAbsoluteUrls'), config);
606
- const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined);
663
+ // Unix-socket requests (own socketPath) commonly pass a path-only url
664
+ // like '/foo'; supply a synthetic base so new URL() can still parse it.
665
+ // Use the own-property value (not config.socketPath) so a polluted
666
+ // prototype cannot influence URL base selection.
667
+ const urlBase = socketPath
668
+ ? 'http://localhost'
669
+ : (platform.hasBrowserEnv ? platform.origin : undefined);
670
+ const parsed = new URL(fullPath, urlBase);
607
671
  const protocol = parsed.protocol || supportedProtocols[0];
608
672
 
609
673
  if (protocol === 'data:') {
@@ -810,11 +874,12 @@ export default isHttpAdapterSupported &&
810
874
  own('paramsSerializer')
811
875
  ).replace(/^\?/, '');
812
876
  } catch (err) {
813
- const customErr = new Error(err.message);
814
- customErr.config = config;
815
- customErr.url = own('url');
816
- customErr.exists = true;
817
- return reject(customErr);
877
+ return reject(
878
+ AxiosError.from(err, AxiosError.ERR_BAD_REQUEST, config, null, null, {
879
+ url: own('url'),
880
+ exists: true
881
+ })
882
+ );
818
883
  }
819
884
 
820
885
  headers.set(
@@ -842,7 +907,6 @@ export default isHttpAdapterSupported &&
842
907
  // cacheable-lookup integration hotfix
843
908
  !utils.isUndefined(lookup) && (options.lookup = lookup);
844
909
 
845
- const socketPath = own('socketPath');
846
910
  if (socketPath) {
847
911
  if (typeof socketPath !== 'string') {
848
912
  return reject(
@@ -880,10 +944,11 @@ export default isHttpAdapterSupported &&
880
944
  options.port = parsed.port;
881
945
  setProxy(
882
946
  options,
883
- own('proxy'),
947
+ configProxy,
884
948
  protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path,
885
949
  false,
886
- httpsAgent
950
+ httpsAgent,
951
+ httpAgent
887
952
  );
888
953
  }
889
954
  let transport;
@@ -979,10 +1044,12 @@ export default isHttpAdapterSupported &&
979
1044
  }
980
1045
  }
981
1046
 
1047
+ // Set an explicit maxBodyLength option for transports that inspect it.
1048
+ // When maxBodyLength is -1 (default/unlimited), use Infinity so
1049
+ // follow-redirects does not fall back to its own 10MB default.
982
1050
  if (maxBodyLength > -1) {
983
1051
  options.maxBodyLength = maxBodyLength;
984
1052
  } else {
985
- // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
986
1053
  options.maxBodyLength = Infinity;
987
1054
  }
988
1055
 
@@ -1205,7 +1272,11 @@ export default isHttpAdapterSupported &&
1205
1272
 
1206
1273
  req.on('socket', function handleRequestSocket(socket) {
1207
1274
  // default interval of sending ack packet is 1 minute
1208
- socket.setKeepAlive(true, 1000 * 60);
1275
+ // proxy agents (e.g. agent-base) may return a generic Duplex stream
1276
+ // that doesn't have setKeepAlive, so guard before calling
1277
+ if (typeof socket.setKeepAlive === 'function') {
1278
+ socket.setKeepAlive(true, 1000 * 60);
1279
+ }
1209
1280
 
1210
1281
  // Install a single 'error' listener per socket (not per request) to avoid
1211
1282
  // accumulating listeners on pooled keep-alive sockets that get reassigned
@@ -1342,4 +1413,5 @@ export default isHttpAdapterSupported &&
1342
1413
  };
1343
1414
 
1344
1415
  export const __setProxy = setProxy;
1416
+ export const __isNodeEnvProxyEnabled = isNodeEnvProxyEnabled;
1345
1417
  export const __isSameOriginRedirect = isSameOriginRedirect;
@@ -218,6 +218,7 @@ export default isXHRAdapterSupported &&
218
218
  config
219
219
  )
220
220
  );
221
+ done();
221
222
  return;
222
223
  }
223
224
 
@@ -75,7 +75,19 @@ function redactConfig(config, redactKeys) {
75
75
  class AxiosError extends Error {
76
76
  static from(error, code, config, request, response, customProps) {
77
77
  const axiosError = new AxiosError(error.message, code || error.code, config, request, response);
78
- axiosError.cause = error;
78
+ // Match native `Error` `cause` semantics: non-enumerable. The wrapped
79
+ // error often carries circular internals (sockets, requests, agents), so
80
+ // an enumerable `cause` makes structured loggers (pino/winston) and any
81
+ // own-property walk throw "Converting circular structure to JSON".
82
+ // Regression from #6982; see #7205. `__proto__: null` mirrors the
83
+ // `message` descriptor below (prototype-pollution-safe descriptor).
84
+ Object.defineProperty(axiosError, 'cause', {
85
+ __proto__: null,
86
+ value: error,
87
+ writable: true,
88
+ enumerable: false,
89
+ configurable: true,
90
+ });
79
91
  axiosError.name = error.name;
80
92
 
81
93
  // Preserve status from the original error if not already set from response
@@ -16,6 +16,7 @@ const headersToObject = (thing) => (thing instanceof AxiosHeaders ? { ...thing }
16
16
  */
17
17
  export default function mergeConfig(config1, config2) {
18
18
  // eslint-disable-next-line no-param-reassign
19
+ config1 = config1 || {};
19
20
  config2 = config2 || {};
20
21
 
21
22
  // Use a null-prototype object so that downstream reads such as `config.auth`
package/lib/env/data.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "1.18.0";
1
+ export const VERSION = "1.18.1";
@@ -46,9 +46,7 @@ prototype.append = function append(name, value) {
46
46
 
47
47
  prototype.toString = function toString(encoder) {
48
48
  const _encode = encoder
49
- ? function (value) {
50
- return encoder.call(this, value, encode);
51
- }
49
+ ? (value) => encoder.call(this, value, encode)
52
50
  : encode;
53
51
 
54
52
  return this._pairs
@@ -32,6 +32,7 @@ export default function buildURL(url, params, options) {
32
32
  if (!params) {
33
33
  return url;
34
34
  }
35
+ url = url || '';
35
36
 
36
37
  const _options = utils.isFunction(options)
37
38
  ? {
@@ -45,7 +45,7 @@ const composeSignals = (signals, timeout) => {
45
45
  signals = null;
46
46
  };
47
47
 
48
- signals.forEach((signal) => signal.addEventListener('abort', onabort));
48
+ signals.forEach((signal) => signal.addEventListener('abort', onabort, { once: true }));
49
49
 
50
50
  const { signal } = controller;
51
51
 
@@ -40,7 +40,11 @@ export default platform.hasStandardBrowserEnv
40
40
  const cookie = cookies[i].replace(/^\s+/, '');
41
41
  const eq = cookie.indexOf('=');
42
42
  if (eq !== -1 && cookie.slice(0, eq) === name) {
43
- return decodeURIComponent(cookie.slice(eq + 1));
43
+ try {
44
+ return decodeURIComponent(cookie.slice(eq + 1));
45
+ } catch (e) {
46
+ return cookie.slice(eq + 1);
47
+ }
44
48
  }
45
49
  }
46
50
  return null;
@@ -42,14 +42,16 @@ export default function fromDataURI(uri, asBlob, options) {
42
42
 
43
43
  // RFC 2397 section 3: default mediatype is text/plain;charset=US-ASCII
44
44
  // Bare `data:,` leaves mime undefined; Blob normalises that to "" per spec.
45
- let mime;
45
+ let mime = '';
46
46
  if (type) {
47
47
  mime = params ? type + params : type;
48
48
  } else if (params) {
49
49
  mime = 'text/plain' + params;
50
50
  }
51
51
 
52
- const buffer = Buffer.from(decodeURIComponent(body), encoding);
52
+ const buffer = encoding === 'base64'
53
+ ? Buffer.from(body, 'base64')
54
+ : Buffer.from(decodeURIComponent(body), encoding);
53
55
 
54
56
  if (asBlob) {
55
57
  if (!_Blob) {
@@ -1,5 +1,6 @@
1
1
  import platform from '../platform/index.js';
2
2
  import utils from '../utils.js';
3
+ import AxiosError from '../core/AxiosError.js';
3
4
  import isURLSameOrigin from './isURLSameOrigin.js';
4
5
  import cookies from './cookies.js';
5
6
  import buildFullPath from '../core/buildFullPath.js';
@@ -15,7 +16,7 @@ function setFormDataHeaders(headers, formHeaders, policy) {
15
16
  return;
16
17
  }
17
18
 
18
- Object.entries(formHeaders).forEach(([key, val]) => {
19
+ Object.entries(formHeaders || {}).forEach(([key, val]) => {
19
20
  if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) {
20
21
  headers.set(key, val);
21
22
  }
@@ -65,10 +66,14 @@ function resolveConfig(config) {
65
66
  const username = utils.getSafeProp(auth, 'username') || '';
66
67
  const password = utils.getSafeProp(auth, 'password') || '';
67
68
 
68
- headers.set(
69
- 'Authorization',
70
- 'Basic ' + btoa(username + ':' + (password ? encodeUTF8(password) : ''))
71
- );
69
+ try {
70
+ headers.set(
71
+ 'Authorization',
72
+ 'Basic ' + btoa(username + ':' + (password ? encodeUTF8(password) : ''))
73
+ );
74
+ } catch (e) {
75
+ throw AxiosError.from(e, AxiosError.ERR_BAD_OPTION_VALUE, config);
76
+ }
72
77
  }
73
78
 
74
79
  if (utils.isFormData(data)) {
@@ -143,7 +143,13 @@ function toFormData(obj, formData, options) {
143
143
  }
144
144
 
145
145
  if (utils.isArrayBuffer(value) || utils.isTypedArray(value)) {
146
- return useBlob && typeof Blob === 'function' ? new Blob([value]) : Buffer.from(value);
146
+ if (useBlob && typeof _Blob === 'function') {
147
+ return new _Blob([value]);
148
+ }
149
+ if (typeof Buffer !== 'undefined') {
150
+ return Buffer.from(value);
151
+ }
152
+ throw new AxiosError('Blob is not supported. Use a Buffer instead.', AxiosError.ERR_NOT_SUPPORT);
147
153
  }
148
154
 
149
155
  return value;
@@ -79,7 +79,7 @@ validators.spelling = function spelling(correctSpelling) {
79
79
  */
80
80
 
81
81
  function assertOptions(options, schema, allowUnknown) {
82
- if (typeof options !== 'object') {
82
+ if (typeof options !== 'object' || options === null) {
83
83
  throw new AxiosError('options must be an object', AxiosError.ERR_BAD_OPTION_VALUE);
84
84
  }
85
85
  const keys = Object.keys(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axios",
3
- "version": "1.18.0",
3
+ "version": "1.18.1",
4
4
  "description": "Promise based HTTP client for the browser and node.js",
5
5
  "main": "./dist/node/axios.cjs",
6
6
  "module": "./index.js",
@@ -87,8 +87,8 @@
87
87
  "Martti Laine (https://github.com/codeclown)",
88
88
  "Xianming Zhong (https://github.com/chinesedfan)",
89
89
  "Shaan Majid (https://github.com/shaanmajid)",
90
- "Willian Agostini (https://github.com/WillianAgostini)",
91
90
  "Remco Haszing (https://github.com/remcohaszing)",
91
+ "Willian Agostini (https://github.com/WillianAgostini)",
92
92
  "Rikki Gibson (https://github.com/RikkiGibson)"
93
93
  ],
94
94
  "sideEffects": false,