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.
- package/CHANGELOG.md +94 -1
- package/README.md +267 -239
- package/dist/axios.js +454 -146
- package/dist/axios.min.js +3 -3
- package/dist/axios.min.js.map +1 -1
- package/dist/browser/axios.cjs +470 -99
- package/dist/esm/axios.js +470 -99
- package/dist/esm/axios.min.js +2 -2
- package/dist/esm/axios.min.js.map +1 -1
- package/dist/node/axios.cjs +638 -201
- package/index.d.cts +10 -3
- package/index.d.ts +6 -1
- package/lib/adapters/fetch.js +190 -35
- package/lib/adapters/http.js +192 -159
- package/lib/core/Axios.js +4 -2
- package/lib/core/AxiosHeaders.js +12 -9
- package/lib/core/buildFullPath.js +29 -1
- package/lib/core/mergeConfig.js +34 -0
- package/lib/defaults/transitional.js +2 -0
- package/lib/env/data.js +1 -1
- package/lib/helpers/Http2Sessions.js +119 -0
- package/lib/helpers/buildURL.js +6 -4
- package/lib/helpers/estimateDataURLDecodedBytes.js +16 -11
- package/lib/helpers/formDataToJSON.js +25 -3
- package/lib/helpers/formDataToStream.js +2 -2
- package/lib/helpers/resolveConfig.js +17 -9
- package/lib/helpers/shouldBypassProxy.js +33 -1
- package/lib/helpers/toFormData.js +41 -11
- package/lib/utils.js +97 -12
- package/package.json +29 -13
- package/dist/axios.js.map +0 -1
- package/dist/browser/axios.cjs.map +0 -1
- package/dist/esm/axios.js.map +0 -1
- package/dist/node/axios.cjs.map +0 -1
package/lib/adapters/http.js
CHANGED
|
@@ -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,
|
|
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
|
-
// (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
587
|
-
|
|
543
|
+
const configTimeout = own('timeout');
|
|
544
|
+
let timeoutErrorMessage = configTimeout
|
|
545
|
+
? 'timeout of ' + configTimeout + 'ms exceeded'
|
|
588
546
|
: 'timeout exceeded';
|
|
589
|
-
const
|
|
590
|
-
if (
|
|
591
|
-
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(
|
|
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 (
|
|
654
|
-
// Use the exact string passed to fromDataURI (
|
|
655
|
-
const dataUrl = String(
|
|
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 >
|
|
616
|
+
if (estimated > maxContentLength) {
|
|
659
617
|
return reject(
|
|
660
618
|
new AxiosError(
|
|
661
|
-
'maxContentLength size of ' +
|
|
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(
|
|
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 (
|
|
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
|
|
834
|
-
const password = configAuth
|
|
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
|
-
|
|
852
|
-
|
|
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 =
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
887
|
-
|
|
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
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
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(
|
|
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 "${
|
|
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 =
|
|
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
|
-
|
|
883
|
+
own('proxy'),
|
|
923
884
|
protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path,
|
|
924
885
|
false,
|
|
925
|
-
|
|
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 ?
|
|
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 (
|
|
909
|
+
} else if (maxRedirects === 0) {
|
|
944
910
|
transport = isHttpsRequest ? https : http;
|
|
945
911
|
isNativeTransport = true;
|
|
946
912
|
} else {
|
|
947
|
-
|
|
948
|
-
|
|
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 (
|
|
959
|
-
options.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 (
|
|
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 (
|
|
1057
|
-
const limit =
|
|
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 (
|
|
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 ' +
|
|
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 (
|
|
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(
|
|
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
|
|
1271
|
-
//
|
|
1272
|
-
//
|
|
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 (
|
|
1275
|
-
const limit =
|
|
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
|
|
250
|
+
data: config && utils.hasOwnProp(config, 'data') ? config.data : undefined,
|
|
249
251
|
})
|
|
250
252
|
);
|
|
251
253
|
};
|
package/lib/core/AxiosHeaders.js
CHANGED
|
@@ -89,7 +89,7 @@ class AxiosHeaders {
|
|
|
89
89
|
const lHeader = normalizeHeader(_header);
|
|
90
90
|
|
|
91
91
|
if (!lHeader) {
|
|
92
|
-
|
|
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.
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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);
|