axios 1.16.1 → 1.18.0

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.
@@ -24,6 +24,7 @@ import { EventEmitter } from 'events';
24
24
  import formDataToStream from '../helpers/formDataToStream.js';
25
25
  import readBlob from '../helpers/readBlob.js';
26
26
  import ZlibHeaderTransformStream from '../helpers/ZlibHeaderTransformStream.js';
27
+ import Http2Sessions from '../helpers/Http2Sessions.js';
27
28
  import callbackify from '../helpers/callbackify.js';
28
29
  import shouldBypassProxy from '../helpers/shouldBypassProxy.js';
29
30
  import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js';
@@ -44,7 +45,15 @@ const brotliOptions = {
44
45
  finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,
45
46
  };
46
47
 
48
+ const zstdOptions = {
49
+ flush: zlib.constants.ZSTD_e_flush,
50
+ finishFlush: zlib.constants.ZSTD_e_flush,
51
+ };
52
+
47
53
  const isBrotliSupported = utils.isFunction(zlib.createBrotliDecompress);
54
+ const isZstdSupported = utils.isFunction(zlib.createZstdDecompress);
55
+ const ACCEPT_ENCODING = 'gzip, compress, deflate' + (isBrotliSupported ? ', br' : '');
56
+ const ACCEPT_ENCODING_WITH_ZSTD = ACCEPT_ENCODING + (isZstdSupported ? ', zstd' : '');
48
57
 
49
58
  const { http: httpFollow, https: httpsFollow } = followRedirects;
50
59
 
@@ -103,6 +112,14 @@ function getTunnelingAgent(agentOptions, userHttpsAgent) {
103
112
  ? { ...userHttpsAgent.options, ...agentOptions }
104
113
  : agentOptions;
105
114
  agent = new HttpsProxyAgent(merged);
115
+ if (userHttpsAgent && userHttpsAgent.options) {
116
+ const originTLSOptions = { ...userHttpsAgent.options };
117
+ const callback = agent.callback;
118
+ agent.callback = function axiosTunnelingAgentCallback(req, opts) {
119
+ // HttpsProxyAgent v5 reads callback opts for the post-CONNECT origin TLS upgrade.
120
+ return callback.call(this, req, { ...originTLSOptions, ...opts });
121
+ };
122
+ }
106
123
  agent[kAxiosInstalledTunnel] = true;
107
124
  cache.set(key, agent);
108
125
  return agent;
@@ -134,114 +151,11 @@ const flushOnFinish = (stream, [throttled, flush]) => {
134
151
  return throttled;
135
152
  };
136
153
 
137
- class Http2Sessions {
138
- constructor() {
139
- this.sessions = Object.create(null);
140
- }
141
-
142
- getSession(authority, options) {
143
- options = Object.assign(
144
- {
145
- sessionTimeout: 1000,
146
- },
147
- options
148
- );
149
-
150
- let authoritySessions = this.sessions[authority];
151
-
152
- if (authoritySessions) {
153
- let len = authoritySessions.length;
154
-
155
- for (let i = 0; i < len; i++) {
156
- const [sessionHandle, sessionOptions] = authoritySessions[i];
157
- if (
158
- !sessionHandle.destroyed &&
159
- !sessionHandle.closed &&
160
- util.isDeepStrictEqual(sessionOptions, options)
161
- ) {
162
- return sessionHandle;
163
- }
164
- }
165
- }
166
-
167
- const session = http2.connect(authority, options);
168
-
169
- let removed;
170
-
171
- const removeSession = () => {
172
- if (removed) {
173
- return;
174
- }
175
-
176
- removed = true;
177
-
178
- let entries = authoritySessions,
179
- len = entries.length,
180
- i = len;
181
-
182
- while (i--) {
183
- if (entries[i][0] === session) {
184
- if (len === 1) {
185
- delete this.sessions[authority];
186
- } else {
187
- entries.splice(i, 1);
188
- }
189
- if (!session.closed) {
190
- session.close();
191
- }
192
- return;
193
- }
194
- }
195
- };
196
-
197
- const originalRequestFn = session.request;
198
-
199
- const { sessionTimeout } = options;
200
-
201
- if (sessionTimeout != null) {
202
- let timer;
203
- let streamsCount = 0;
204
-
205
- session.request = function () {
206
- const stream = originalRequestFn.apply(this, arguments);
207
-
208
- streamsCount++;
209
-
210
- if (timer) {
211
- clearTimeout(timer);
212
- timer = null;
213
- }
214
-
215
- stream.once('close', () => {
216
- if (!--streamsCount) {
217
- timer = setTimeout(() => {
218
- timer = null;
219
- removeSession();
220
- }, sessionTimeout);
221
- }
222
- });
223
-
224
- return stream;
225
- };
226
- }
227
-
228
- session.once('close', removeSession);
229
-
230
- let entry = [session, options];
231
-
232
- authoritySessions
233
- ? authoritySessions.push(entry)
234
- : (authoritySessions = this.sessions[authority] = [entry]);
235
-
236
- return session;
237
- }
238
- }
239
-
240
154
  const http2Sessions = new Http2Sessions();
241
155
 
242
156
  /**
243
- * If the proxy or config beforeRedirects functions are defined, call them with the options
244
- * object.
157
+ * If the proxy, auth, sensitive header, or config beforeRedirects functions are defined,
158
+ * call them with the options object.
245
159
  *
246
160
  * @param {Object<string, any>} options - The options object that was passed to the request.
247
161
  *
@@ -251,11 +165,42 @@ function dispatchBeforeRedirect(options, responseDetails, requestDetails) {
251
165
  if (options.beforeRedirects.proxy) {
252
166
  options.beforeRedirects.proxy(options);
253
167
  }
168
+ if (options.beforeRedirects.auth) {
169
+ options.beforeRedirects.auth(options);
170
+ }
171
+ if (options.beforeRedirects.sensitiveHeaders) {
172
+ options.beforeRedirects.sensitiveHeaders(options, requestDetails);
173
+ }
254
174
  if (options.beforeRedirects.config) {
255
175
  options.beforeRedirects.config(options, responseDetails, requestDetails);
256
176
  }
257
177
  }
258
178
 
179
+ function stripMatchingHeaders(headers, sensitiveSet) {
180
+ if (!headers) {
181
+ return;
182
+ }
183
+
184
+ Object.keys(headers).forEach((header) => {
185
+ if (sensitiveSet.has(header.toLowerCase())) {
186
+ delete headers[header];
187
+ }
188
+ });
189
+ }
190
+
191
+ function isSameOriginRedirect(redirectOptions, requestDetails) {
192
+ if (!requestDetails) {
193
+ return false;
194
+ }
195
+
196
+ try {
197
+ return new URL(requestDetails.url).origin === new URL(redirectOptions.href).origin;
198
+ } catch (e) {
199
+ // If origin comparison fails, treat the redirect as unsafe.
200
+ return false;
201
+ }
202
+ }
203
+
259
204
  /**
260
205
  * If the proxy or config afterRedirects functions are defined, call them with the options
261
206
  *
@@ -373,7 +318,7 @@ function setProxy(options, configProxy, location, isRedirect, configHttpsAgent)
373
318
  }
374
319
  const tunnelingAgent = getTunnelingAgent(agentOptions, configHttpsAgent);
375
320
  // Set both: `options.agent` is consumed by the native https.request path
376
- // (config.maxRedirects === 0); `options.agents.https` is consumed by
321
+ // (maxRedirects === 0); `options.agents.https` is consumed by
377
322
  // follow-redirects, which ignores `options.agent` when `options.agents`
378
323
  // is present.
379
324
  options.agent = tunnelingAgent;
@@ -517,7 +462,13 @@ const http2Transport = {
517
462
  export default isHttpAdapterSupported &&
518
463
  function httpAdapter(config) {
519
464
  return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {
520
- const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined);
465
+ // Read config pollution-safely: own properties and members inherited from
466
+ // a non-Object.prototype source (e.g. an Object.create(defaults) template)
467
+ // are honored, but values injected onto a polluted Object.prototype are
468
+ // ignored. All behavior-affecting reads in this adapter go through own()
469
+ // so the protection boundary stays consistent.
470
+ const own = (key) => utils.getSafeProp(config, key);
471
+ const transitional = own('transitional') || transitionalDefaults;
521
472
  let data = own('data');
522
473
  let lookup = own('lookup');
523
474
  let family = own('family');
@@ -526,7 +477,13 @@ export default isHttpAdapterSupported &&
526
477
  let http2Options = own('http2Options');
527
478
  const responseType = own('responseType');
528
479
  const responseEncoding = own('responseEncoding');
529
- const method = config.method.toUpperCase();
480
+ const httpAgent = own('httpAgent');
481
+ const httpsAgent = own('httpsAgent');
482
+ const method = own('method').toUpperCase();
483
+ const maxRedirects = own('maxRedirects');
484
+ const maxBodyLength = own('maxBodyLength');
485
+ const maxContentLength = own('maxContentLength');
486
+ const decompress = own('decompress');
530
487
  let isDone;
531
488
  let rejected = false;
532
489
  let req;
@@ -571,7 +528,7 @@ export default isHttpAdapterSupported &&
571
528
  !reason || reason.type ? new CanceledError(null, config, req) : reason
572
529
  );
573
530
  } catch (err) {
574
- console.warn('emit error', err);
531
+ // ignore emit errors
575
532
  }
576
533
  }
577
534
 
@@ -583,12 +540,13 @@ export default isHttpAdapterSupported &&
583
540
  }
584
541
 
585
542
  function createTimeoutError() {
586
- let timeoutErrorMessage = config.timeout
587
- ? 'timeout of ' + config.timeout + 'ms exceeded'
543
+ const configTimeout = own('timeout');
544
+ let timeoutErrorMessage = configTimeout
545
+ ? 'timeout of ' + configTimeout + 'ms exceeded'
588
546
  : 'timeout exceeded';
589
- const transitional = config.transitional || transitionalDefaults;
590
- if (config.timeoutErrorMessage) {
591
- timeoutErrorMessage = config.timeoutErrorMessage;
547
+ const configTimeoutErrorMessage = own('timeoutErrorMessage');
548
+ if (configTimeoutErrorMessage) {
549
+ timeoutErrorMessage = configTimeoutErrorMessage;
592
550
  }
593
551
  return new AxiosError(
594
552
  timeoutErrorMessage,
@@ -644,21 +602,21 @@ export default isHttpAdapterSupported &&
644
602
  });
645
603
 
646
604
  // Parse url
647
- const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
605
+ const fullPath = buildFullPath(own('baseURL'), own('url'), own('allowAbsoluteUrls'), config);
648
606
  const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined);
649
607
  const protocol = parsed.protocol || supportedProtocols[0];
650
608
 
651
609
  if (protocol === 'data:') {
652
610
  // Apply the same semantics as HTTP: only enforce if a finite, non-negative cap is set.
653
- if (config.maxContentLength > -1) {
654
- // Use the exact string passed to fromDataURI (config.url); fall back to fullPath if needed.
655
- const dataUrl = String(config.url || fullPath || '');
611
+ if (maxContentLength > -1) {
612
+ // Use the exact string passed to fromDataURI (the configured url); fall back to fullPath if needed.
613
+ const dataUrl = String(own('url') || fullPath || '');
656
614
  const estimated = estimateDataURLDecodedBytes(dataUrl);
657
615
 
658
- if (estimated > config.maxContentLength) {
616
+ if (estimated > maxContentLength) {
659
617
  return reject(
660
618
  new AxiosError(
661
- 'maxContentLength size of ' + config.maxContentLength + ' exceeded',
619
+ 'maxContentLength size of ' + maxContentLength + ' exceeded',
662
620
  AxiosError.ERR_BAD_RESPONSE,
663
621
  config
664
622
  )
@@ -678,7 +636,7 @@ export default isHttpAdapterSupported &&
678
636
  }
679
637
 
680
638
  try {
681
- convertedData = fromDataURI(config.url, responseType === 'blob', {
639
+ convertedData = fromDataURI(own('url'), responseType === 'blob', {
682
640
  Blob: config.env && config.env.Blob,
683
641
  });
684
642
  } catch (err) {
@@ -778,7 +736,7 @@ export default isHttpAdapterSupported &&
778
736
  // Add Content-Length header if data exists
779
737
  headers.setContentLength(data.length, false);
780
738
 
781
- if (config.maxBodyLength > -1 && data.length > config.maxBodyLength) {
739
+ if (maxBodyLength > -1 && data.length > maxBodyLength) {
782
740
  return reject(
783
741
  new AxiosError(
784
742
  'Request body larger than maxBodyLength limit',
@@ -830,12 +788,12 @@ export default isHttpAdapterSupported &&
830
788
  let auth = undefined;
831
789
  const configAuth = own('auth');
832
790
  if (configAuth) {
833
- const username = configAuth.username || '';
834
- const password = configAuth.password || '';
791
+ const username = utils.getSafeProp(configAuth, 'username') || '';
792
+ const password = utils.getSafeProp(configAuth, 'password') || '';
835
793
  auth = username + ':' + password;
836
794
  }
837
795
 
838
- if (!auth && parsed.username) {
796
+ if (!auth && (parsed.username || parsed.password)) {
839
797
  const urlUsername = decodeURIComponentSafe(parsed.username);
840
798
  const urlPassword = decodeURIComponentSafe(parsed.password);
841
799
  auth = urlUsername + ':' + urlPassword;
@@ -848,20 +806,21 @@ export default isHttpAdapterSupported &&
848
806
  try {
849
807
  path = buildURL(
850
808
  parsed.pathname + parsed.search,
851
- config.params,
852
- config.paramsSerializer
809
+ own('params'),
810
+ own('paramsSerializer')
853
811
  ).replace(/^\?/, '');
854
812
  } catch (err) {
855
813
  const customErr = new Error(err.message);
856
814
  customErr.config = config;
857
- customErr.url = config.url;
815
+ customErr.url = own('url');
858
816
  customErr.exists = true;
859
817
  return reject(customErr);
860
818
  }
861
819
 
862
820
  headers.set(
863
821
  'Accept-Encoding',
864
- 'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''),
822
+ utils.hasOwnProp(transitional, 'advertiseZstdAcceptEncoding') &&
823
+ transitional.advertiseZstdAcceptEncoding === true ? ACCEPT_ENCODING_WITH_ZSTD : ACCEPT_ENCODING,
865
824
  false
866
825
  );
867
826
 
@@ -871,7 +830,7 @@ export default isHttpAdapterSupported &&
871
830
  path,
872
831
  method: method,
873
832
  headers: toByteStringHeaderObject(headers),
874
- agents: { http: config.httpAgent, https: config.httpsAgent },
833
+ agents: { http: httpAgent, https: httpsAgent },
875
834
  auth,
876
835
  protocol,
877
836
  family,
@@ -883,19 +842,21 @@ export default isHttpAdapterSupported &&
883
842
  // cacheable-lookup integration hotfix
884
843
  !utils.isUndefined(lookup) && (options.lookup = lookup);
885
844
 
886
- if (config.socketPath) {
887
- if (typeof config.socketPath !== 'string') {
845
+ const socketPath = own('socketPath');
846
+ if (socketPath) {
847
+ if (typeof socketPath !== 'string') {
888
848
  return reject(
889
849
  new AxiosError('socketPath must be a string', AxiosError.ERR_BAD_OPTION_VALUE, config)
890
850
  );
891
851
  }
892
852
 
893
- if (config.allowedSocketPaths != null) {
894
- const allowed = Array.isArray(config.allowedSocketPaths)
895
- ? config.allowedSocketPaths
896
- : [config.allowedSocketPaths];
853
+ const allowedSocketPaths = own('allowedSocketPaths');
854
+ if (allowedSocketPaths != null) {
855
+ const allowed = Array.isArray(allowedSocketPaths)
856
+ ? allowedSocketPaths
857
+ : [allowedSocketPaths];
897
858
 
898
- const resolvedSocket = resolvePath(config.socketPath);
859
+ const resolvedSocket = resolvePath(socketPath);
899
860
  const isAllowed = allowed.some(
900
861
  (entry) => typeof entry === 'string' && resolvePath(entry) === resolvedSocket
901
862
  );
@@ -903,7 +864,7 @@ export default isHttpAdapterSupported &&
903
864
  if (!isAllowed) {
904
865
  return reject(
905
866
  new AxiosError(
906
- `socketPath "${config.socketPath}" is not permitted by allowedSocketPaths`,
867
+ `socketPath "${socketPath}" is not permitted by allowedSocketPaths`,
907
868
  AxiosError.ERR_BAD_OPTION_VALUE,
908
869
  config
909
870
  )
@@ -911,7 +872,7 @@ export default isHttpAdapterSupported &&
911
872
  }
912
873
  }
913
874
 
914
- options.socketPath = config.socketPath;
875
+ options.socketPath = socketPath;
915
876
  } else {
916
877
  options.hostname = parsed.hostname.startsWith('[')
917
878
  ? parsed.hostname.slice(1, -1)
@@ -919,19 +880,24 @@ export default isHttpAdapterSupported &&
919
880
  options.port = parsed.port;
920
881
  setProxy(
921
882
  options,
922
- config.proxy,
883
+ own('proxy'),
923
884
  protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path,
924
885
  false,
925
- config.httpsAgent
886
+ httpsAgent
926
887
  );
927
888
  }
928
889
  let transport;
929
890
  let isNativeTransport = false;
891
+ // True only for the follow-redirects transport, which applies
892
+ // options.maxBodyLength itself. Every other transport (http2, native
893
+ // http/https, a user-supplied custom transport) needs the explicit
894
+ // byte-counting pipeline below to enforce maxBodyLength on streamed uploads.
895
+ let transportEnforcesMaxBodyLength = false;
930
896
  const isHttpsRequest = isHttps.test(options.protocol);
931
897
  // Don't clobber a CONNECT-tunneling agent installed by setProxy() for an
932
898
  // HTTPS target.
933
899
  if (options.agent == null) {
934
- options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
900
+ options.agent = isHttpsRequest ? httpsAgent : httpAgent;
935
901
  }
936
902
 
937
903
  if (isHttp2) {
@@ -940,23 +906,81 @@ export default isHttpAdapterSupported &&
940
906
  const configTransport = own('transport');
941
907
  if (configTransport) {
942
908
  transport = configTransport;
943
- } else if (config.maxRedirects === 0) {
909
+ } else if (maxRedirects === 0) {
944
910
  transport = isHttpsRequest ? https : http;
945
911
  isNativeTransport = true;
946
912
  } else {
947
- if (config.maxRedirects) {
948
- options.maxRedirects = config.maxRedirects;
913
+ transportEnforcesMaxBodyLength = true;
914
+ options.sensitiveHeaders = [];
915
+ if (maxRedirects) {
916
+ options.maxRedirects = maxRedirects;
949
917
  }
950
918
  const configBeforeRedirect = own('beforeRedirect');
951
919
  if (configBeforeRedirect) {
952
920
  options.beforeRedirects.config = configBeforeRedirect;
953
921
  }
922
+ if (auth) {
923
+ // Restore HTTP Basic credentials on same-origin redirects only.
924
+ // follow-redirects >= 1.15.8 strips Authorization on every redirect (see #6929);
925
+ // cross-origin stripping is the documented mitigation for T-R2 in THREATMODEL.md
926
+ // and is preserved by deliberately not restoring on origin change.
927
+ const requestOrigin = parsed.origin;
928
+ const authToRestore = auth;
929
+ options.beforeRedirects.auth = function beforeRedirectAuth(redirectOptions) {
930
+ try {
931
+ if (new URL(redirectOptions.href).origin === requestOrigin) {
932
+ redirectOptions.auth = authToRestore;
933
+ }
934
+ } catch (e) {
935
+ // ignore malformed URL: leaving auth stripped is fail-safe
936
+ }
937
+ };
938
+ }
939
+ const sensitiveHeaders = own('sensitiveHeaders');
940
+ if (sensitiveHeaders != null) {
941
+ if (!utils.isArray(sensitiveHeaders)) {
942
+ return reject(
943
+ new AxiosError(
944
+ 'sensitiveHeaders must be an array of strings',
945
+ AxiosError.ERR_BAD_OPTION_VALUE,
946
+ config
947
+ )
948
+ );
949
+ }
950
+
951
+ const sensitiveSet = new Set();
952
+ for (const header of sensitiveHeaders) {
953
+ if (!utils.isString(header)) {
954
+ return reject(
955
+ new AxiosError(
956
+ 'sensitiveHeaders must be an array of strings',
957
+ AxiosError.ERR_BAD_OPTION_VALUE,
958
+ config
959
+ )
960
+ );
961
+ }
962
+
963
+ sensitiveSet.add(header.toLowerCase());
964
+ }
965
+
966
+ if (sensitiveSet.size) {
967
+ options.sensitiveHeaders = Array.from(sensitiveSet);
968
+ options.beforeRedirects.sensitiveHeaders = function beforeRedirectSensitiveHeaders(
969
+ redirectOptions,
970
+ requestDetails
971
+ ) {
972
+ if (!isSameOriginRedirect(redirectOptions, requestDetails)) {
973
+ stripMatchingHeaders(redirectOptions.headers, sensitiveSet);
974
+ }
975
+ };
976
+ }
977
+ }
954
978
  transport = isHttpsRequest ? httpsFollow : httpFollow;
955
979
  }
956
980
  }
957
981
 
958
- if (config.maxBodyLength > -1) {
959
- options.maxBodyLength = config.maxBodyLength;
982
+ if (maxBodyLength > -1) {
983
+ options.maxBodyLength = maxBodyLength;
960
984
  } else {
961
985
  // follow-redirects does not skip comparison, so it should always succeed for axios -1 unlimited
962
986
  options.maxBodyLength = Infinity;
@@ -1004,7 +1028,7 @@ export default isHttpAdapterSupported &&
1004
1028
  const lastRequest = res.req || req;
1005
1029
 
1006
1030
  // if decompress disabled we should not decompress
1007
- if (config.decompress !== false && res.headers['content-encoding']) {
1031
+ if (decompress !== false && res.headers['content-encoding']) {
1008
1032
  // if no content, but headers still say that it is encoded,
1009
1033
  // remove the header not confuse downstream operations
1010
1034
  if (method === 'HEAD' || res.statusCode === 204) {
@@ -1037,6 +1061,13 @@ export default isHttpAdapterSupported &&
1037
1061
  streams.push(zlib.createBrotliDecompress(brotliOptions));
1038
1062
  delete res.headers['content-encoding'];
1039
1063
  }
1064
+ break;
1065
+ case 'zstd':
1066
+ if (isZstdSupported) {
1067
+ streams.push(zlib.createZstdDecompress(zstdOptions));
1068
+ delete res.headers['content-encoding'];
1069
+ }
1070
+ break;
1040
1071
  }
1041
1072
  }
1042
1073
 
@@ -1053,8 +1084,8 @@ export default isHttpAdapterSupported &&
1053
1084
  if (responseType === 'stream') {
1054
1085
  // Enforce maxContentLength on streamed responses; previously this
1055
1086
  // was applied only to buffered responses.
1056
- if (config.maxContentLength > -1) {
1057
- const limit = config.maxContentLength;
1087
+ if (maxContentLength > -1) {
1088
+ const limit = maxContentLength;
1058
1089
  const source = responseStream;
1059
1090
  async function* enforceMaxContentLength() {
1060
1091
  let totalResponseBytes = 0;
@@ -1086,13 +1117,13 @@ export default isHttpAdapterSupported &&
1086
1117
  totalResponseBytes += chunk.length;
1087
1118
 
1088
1119
  // make sure the content length is not over the maxContentLength if specified
1089
- if (config.maxContentLength > -1 && totalResponseBytes > config.maxContentLength) {
1120
+ if (maxContentLength > -1 && totalResponseBytes > maxContentLength) {
1090
1121
  // stream.destroy() emit aborted event before calling reject() on Node.js v16
1091
1122
  rejected = true;
1092
1123
  responseStream.destroy();
1093
1124
  abort(
1094
1125
  new AxiosError(
1095
- 'maxContentLength size of ' + config.maxContentLength + ' exceeded',
1126
+ 'maxContentLength size of ' + maxContentLength + ' exceeded',
1096
1127
  AxiosError.ERR_BAD_RESPONSE,
1097
1128
  config,
1098
1129
  lastRequest
@@ -1207,9 +1238,9 @@ export default isHttpAdapterSupported &&
1207
1238
  });
1208
1239
 
1209
1240
  // Handle request timeout
1210
- if (config.timeout) {
1241
+ if (own('timeout')) {
1211
1242
  // This is forcing a int timeout to avoid problems if the `req` interface doesn't handle other types.
1212
- const timeout = parseInt(config.timeout, 10);
1243
+ const timeout = parseInt(own('timeout'), 10);
1213
1244
 
1214
1245
  if (Number.isNaN(timeout)) {
1215
1246
  abort(
@@ -1267,12 +1298,13 @@ export default isHttpAdapterSupported &&
1267
1298
  }
1268
1299
  });
1269
1300
 
1270
- // Enforce maxBodyLength for streamed uploads on the native http/https
1271
- // transport (maxRedirects === 0); follow-redirects enforces it on the
1272
- // other path.
1301
+ // Enforce maxBodyLength for streamed uploads on every transport that
1302
+ // does not apply options.maxBodyLength itself (native http/https, http2,
1303
+ // and user-supplied custom transports). The follow-redirects transport
1304
+ // enforces it on the redirected HTTP/1 path.
1273
1305
  let uploadStream = data;
1274
- if (config.maxBodyLength > -1 && config.maxRedirects === 0) {
1275
- const limit = config.maxBodyLength;
1306
+ if (maxBodyLength > -1 && !transportEnforcesMaxBodyLength) {
1307
+ const limit = maxBodyLength;
1276
1308
  let bytesSent = 0;
1277
1309
  uploadStream = stream.pipeline(
1278
1310
  [
@@ -1310,3 +1342,4 @@ export default isHttpAdapterSupported &&
1310
1342
  };
1311
1343
 
1312
1344
  export const __setProxy = setProxy;
1345
+ export const __isSameOriginRedirect = isSameOriginRedirect;
package/lib/core/Axios.js CHANGED
@@ -101,6 +101,8 @@ class Axios {
101
101
  forcedJSONParsing: validators.transitional(validators.boolean),
102
102
  clarifyTimeoutError: validators.transitional(validators.boolean),
103
103
  legacyInterceptorReqResOrdering: validators.transitional(validators.boolean),
104
+ advertiseZstdAcceptEncoding: validators.transitional(validators.boolean),
105
+ validateStatusUndefinedResolves: validators.transitional(validators.boolean),
104
106
  },
105
107
  false
106
108
  );
@@ -232,7 +234,7 @@ class Axios {
232
234
 
233
235
  getUri(config) {
234
236
  config = mergeConfig(this.defaults, config);
235
- const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
237
+ const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls, config);
236
238
  return buildURL(fullPath, config.params, config.paramsSerializer);
237
239
  }
238
240
  }
@@ -245,7 +247,7 @@ utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData
245
247
  mergeConfig(config || {}, {
246
248
  method,
247
249
  url,
248
- data: (config || {}).data,
250
+ data: config && utils.hasOwnProp(config, 'data') ? config.data : undefined,
249
251
  })
250
252
  );
251
253
  };
@@ -89,7 +89,7 @@ class AxiosHeaders {
89
89
  const lHeader = normalizeHeader(_header);
90
90
 
91
91
  if (!lHeader) {
92
- throw new Error('header name must be a non-empty string');
92
+ return;
93
93
  }
94
94
 
95
95
  const key = utils.findKey(self, lHeader);
@@ -111,20 +111,23 @@ class AxiosHeaders {
111
111
  setHeaders(header, valueOrRewrite);
112
112
  } else if (utils.isString(header) && (header = header.trim()) && !isValidHeaderName(header)) {
113
113
  setHeaders(parseHeaders(header), valueOrRewrite);
114
- } else if (utils.isObject(header) && utils.isIterable(header)) {
115
- let obj = {},
114
+ } else if (utils.isObject(header) && utils.isSafeIterable(header)) {
115
+ let obj = Object.create(null),
116
116
  dest,
117
117
  key;
118
118
  for (const entry of header) {
119
119
  if (!utils.isArray(entry)) {
120
- throw TypeError('Object iterator must return a key-value pair');
120
+ throw new TypeError('Object iterator must return a key-value pair');
121
121
  }
122
122
 
123
- obj[(key = entry[0])] = (dest = obj[key])
124
- ? utils.isArray(dest)
125
- ? [...dest, entry[1]]
126
- : [dest, entry[1]]
127
- : entry[1];
123
+ key = entry[0];
124
+
125
+ if (utils.hasOwnProp(obj, key)) {
126
+ dest = obj[key];
127
+ obj[key] = utils.isArray(dest) ? [...dest, entry[1]] : [dest, entry[1]];
128
+ } else {
129
+ obj[key] = entry[1];
130
+ }
128
131
  }
129
132
 
130
133
  setHeaders(obj, valueOrRewrite);