axios 1.16.0 → 1.17.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 +113 -0
- package/README.md +280 -245
- package/dist/axios.js +192 -72
- package/dist/axios.min.js +2 -2
- package/dist/axios.min.js.map +1 -1
- package/dist/browser/axios.cjs +215 -77
- package/dist/esm/axios.js +215 -77
- package/dist/esm/axios.min.js +2 -2
- package/dist/esm/axios.min.js.map +1 -1
- package/dist/node/axios.cjs +453 -190
- package/index.d.cts +9 -4
- package/index.d.ts +5 -2
- package/lib/adapters/fetch.js +85 -2
- package/lib/adapters/http.js +201 -147
- package/lib/adapters/xhr.js +2 -1
- package/lib/core/Axios.js +1 -0
- package/lib/core/AxiosHeaders.js +3 -35
- package/lib/defaults/transitional.js +1 -0
- package/lib/env/data.js +1 -1
- package/lib/helpers/Http2Sessions.js +119 -0
- package/lib/helpers/buildURL.js +1 -1
- package/lib/helpers/composeSignals.js +48 -47
- package/lib/helpers/formDataToJSON.js +1 -1
- package/lib/helpers/formDataToStream.js +2 -2
- package/lib/helpers/fromDataURI.js +18 -5
- package/lib/helpers/progressEventReducer.js +3 -0
- package/lib/helpers/resolveConfig.js +12 -6
- package/lib/helpers/sanitizeHeaderValue.js +60 -0
- package/lib/helpers/toFormData.js +1 -1
- package/lib/utils.js +31 -9
- package/package.json +31 -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
|
@@ -3,6 +3,7 @@ import settle from '../core/settle.js';
|
|
|
3
3
|
import buildFullPath from '../core/buildFullPath.js';
|
|
4
4
|
import buildURL from '../helpers/buildURL.js';
|
|
5
5
|
import { getProxyForUrl } from 'proxy-from-env';
|
|
6
|
+
import HttpsProxyAgent from 'https-proxy-agent';
|
|
6
7
|
import http from 'http';
|
|
7
8
|
import https from 'https';
|
|
8
9
|
import http2 from 'http2';
|
|
@@ -23,8 +24,10 @@ import { EventEmitter } from 'events';
|
|
|
23
24
|
import formDataToStream from '../helpers/formDataToStream.js';
|
|
24
25
|
import readBlob from '../helpers/readBlob.js';
|
|
25
26
|
import ZlibHeaderTransformStream from '../helpers/ZlibHeaderTransformStream.js';
|
|
27
|
+
import Http2Sessions from '../helpers/Http2Sessions.js';
|
|
26
28
|
import callbackify from '../helpers/callbackify.js';
|
|
27
29
|
import shouldBypassProxy from '../helpers/shouldBypassProxy.js';
|
|
30
|
+
import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js';
|
|
28
31
|
import {
|
|
29
32
|
progressEventReducer,
|
|
30
33
|
progressEventDecorator,
|
|
@@ -42,7 +45,15 @@ const brotliOptions = {
|
|
|
42
45
|
finishFlush: zlib.constants.BROTLI_OPERATION_FLUSH,
|
|
43
46
|
};
|
|
44
47
|
|
|
48
|
+
const zstdOptions = {
|
|
49
|
+
flush: zlib.constants.ZSTD_e_flush,
|
|
50
|
+
finishFlush: zlib.constants.ZSTD_e_flush,
|
|
51
|
+
};
|
|
52
|
+
|
|
45
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' : '');
|
|
46
57
|
|
|
47
58
|
const { http: httpFollow, https: httpsFollow } = followRedirects;
|
|
48
59
|
|
|
@@ -67,6 +78,53 @@ function setFormDataHeaders(headers, formHeaders, policy) {
|
|
|
67
78
|
const kAxiosSocketListener = Symbol('axios.http.socketListener');
|
|
68
79
|
const kAxiosCurrentReq = Symbol('axios.http.currentReq');
|
|
69
80
|
|
|
81
|
+
// Tags HttpsProxyAgent instances installed by setProxy() so the redirect path
|
|
82
|
+
// can strip them without clobbering a user-supplied agent that happens to be
|
|
83
|
+
// an HttpsProxyAgent.
|
|
84
|
+
const kAxiosInstalledTunnel = Symbol('axios.http.installedTunnel');
|
|
85
|
+
|
|
86
|
+
// Cache of CONNECT-tunneling agents keyed by proxy config so repeat requests
|
|
87
|
+
// through the same proxy reuse a single agent (and its socket pool). The
|
|
88
|
+
// keyspace is bounded by the set of distinct proxy configs the process uses,
|
|
89
|
+
// so unbounded growth is not a concern in practice.
|
|
90
|
+
const tunnelingAgentCache = new Map();
|
|
91
|
+
const tunnelingAgentCacheUser = new WeakMap();
|
|
92
|
+
|
|
93
|
+
function getTunnelingAgent(agentOptions, userHttpsAgent) {
|
|
94
|
+
const key =
|
|
95
|
+
agentOptions.protocol +
|
|
96
|
+
'//' +
|
|
97
|
+
agentOptions.hostname +
|
|
98
|
+
':' +
|
|
99
|
+
(agentOptions.port || '') +
|
|
100
|
+
'#' +
|
|
101
|
+
(agentOptions.auth || '');
|
|
102
|
+
const cache = userHttpsAgent
|
|
103
|
+
? (tunnelingAgentCacheUser.get(userHttpsAgent) ||
|
|
104
|
+
tunnelingAgentCacheUser.set(userHttpsAgent, new Map()).get(userHttpsAgent))
|
|
105
|
+
: tunnelingAgentCache;
|
|
106
|
+
let agent = cache.get(key);
|
|
107
|
+
if (agent) return agent;
|
|
108
|
+
// Forward the user's TLS options (custom CA, rejectUnauthorized, client cert,
|
|
109
|
+
// etc.) into the tunneling agent so they apply to the origin TLS upgrade
|
|
110
|
+
// performed after CONNECT. Our proxy fields take precedence on conflict.
|
|
111
|
+
const merged = userHttpsAgent && userHttpsAgent.options
|
|
112
|
+
? { ...userHttpsAgent.options, ...agentOptions }
|
|
113
|
+
: agentOptions;
|
|
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
|
+
}
|
|
123
|
+
agent[kAxiosInstalledTunnel] = true;
|
|
124
|
+
cache.set(key, agent);
|
|
125
|
+
return agent;
|
|
126
|
+
}
|
|
127
|
+
|
|
70
128
|
const supportedProtocols = platform.protocols.map((protocol) => {
|
|
71
129
|
return protocol + ':';
|
|
72
130
|
});
|
|
@@ -93,114 +151,11 @@ const flushOnFinish = (stream, [throttled, flush]) => {
|
|
|
93
151
|
return throttled;
|
|
94
152
|
};
|
|
95
153
|
|
|
96
|
-
class Http2Sessions {
|
|
97
|
-
constructor() {
|
|
98
|
-
this.sessions = Object.create(null);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getSession(authority, options) {
|
|
102
|
-
options = Object.assign(
|
|
103
|
-
{
|
|
104
|
-
sessionTimeout: 1000,
|
|
105
|
-
},
|
|
106
|
-
options
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
let authoritySessions = this.sessions[authority];
|
|
110
|
-
|
|
111
|
-
if (authoritySessions) {
|
|
112
|
-
let len = authoritySessions.length;
|
|
113
|
-
|
|
114
|
-
for (let i = 0; i < len; i++) {
|
|
115
|
-
const [sessionHandle, sessionOptions] = authoritySessions[i];
|
|
116
|
-
if (
|
|
117
|
-
!sessionHandle.destroyed &&
|
|
118
|
-
!sessionHandle.closed &&
|
|
119
|
-
util.isDeepStrictEqual(sessionOptions, options)
|
|
120
|
-
) {
|
|
121
|
-
return sessionHandle;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const session = http2.connect(authority, options);
|
|
127
|
-
|
|
128
|
-
let removed;
|
|
129
|
-
|
|
130
|
-
const removeSession = () => {
|
|
131
|
-
if (removed) {
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
removed = true;
|
|
136
|
-
|
|
137
|
-
let entries = authoritySessions,
|
|
138
|
-
len = entries.length,
|
|
139
|
-
i = len;
|
|
140
|
-
|
|
141
|
-
while (i--) {
|
|
142
|
-
if (entries[i][0] === session) {
|
|
143
|
-
if (len === 1) {
|
|
144
|
-
delete this.sessions[authority];
|
|
145
|
-
} else {
|
|
146
|
-
entries.splice(i, 1);
|
|
147
|
-
}
|
|
148
|
-
if (!session.closed) {
|
|
149
|
-
session.close();
|
|
150
|
-
}
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
const originalRequestFn = session.request;
|
|
157
|
-
|
|
158
|
-
const { sessionTimeout } = options;
|
|
159
|
-
|
|
160
|
-
if (sessionTimeout != null) {
|
|
161
|
-
let timer;
|
|
162
|
-
let streamsCount = 0;
|
|
163
|
-
|
|
164
|
-
session.request = function () {
|
|
165
|
-
const stream = originalRequestFn.apply(this, arguments);
|
|
166
|
-
|
|
167
|
-
streamsCount++;
|
|
168
|
-
|
|
169
|
-
if (timer) {
|
|
170
|
-
clearTimeout(timer);
|
|
171
|
-
timer = null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
stream.once('close', () => {
|
|
175
|
-
if (!--streamsCount) {
|
|
176
|
-
timer = setTimeout(() => {
|
|
177
|
-
timer = null;
|
|
178
|
-
removeSession();
|
|
179
|
-
}, sessionTimeout);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
return stream;
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
session.once('close', removeSession);
|
|
188
|
-
|
|
189
|
-
let entry = [session, options];
|
|
190
|
-
|
|
191
|
-
authoritySessions
|
|
192
|
-
? authoritySessions.push(entry)
|
|
193
|
-
: (authoritySessions = this.sessions[authority] = [entry]);
|
|
194
|
-
|
|
195
|
-
return session;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
154
|
const http2Sessions = new Http2Sessions();
|
|
200
155
|
|
|
201
156
|
/**
|
|
202
|
-
* If the proxy or config beforeRedirects functions are defined, call them
|
|
203
|
-
* object.
|
|
157
|
+
* If the proxy, auth, or config beforeRedirects functions are defined, call them
|
|
158
|
+
* with the options object.
|
|
204
159
|
*
|
|
205
160
|
* @param {Object<string, any>} options - The options object that was passed to the request.
|
|
206
161
|
*
|
|
@@ -210,6 +165,9 @@ function dispatchBeforeRedirect(options, responseDetails, requestDetails) {
|
|
|
210
165
|
if (options.beforeRedirects.proxy) {
|
|
211
166
|
options.beforeRedirects.proxy(options);
|
|
212
167
|
}
|
|
168
|
+
if (options.beforeRedirects.auth) {
|
|
169
|
+
options.beforeRedirects.auth(options);
|
|
170
|
+
}
|
|
213
171
|
if (options.beforeRedirects.config) {
|
|
214
172
|
options.beforeRedirects.config(options, responseDetails, requestDetails);
|
|
215
173
|
}
|
|
@@ -224,7 +182,7 @@ function dispatchBeforeRedirect(options, responseDetails, requestDetails) {
|
|
|
224
182
|
*
|
|
225
183
|
* @returns {http.ClientRequestArgs}
|
|
226
184
|
*/
|
|
227
|
-
function setProxy(options, configProxy, location, isRedirect) {
|
|
185
|
+
function setProxy(options, configProxy, location, isRedirect, configHttpsAgent) {
|
|
228
186
|
let proxy = configProxy;
|
|
229
187
|
if (!proxy && proxy !== false) {
|
|
230
188
|
const proxyUrl = getProxyForUrl(location);
|
|
@@ -245,6 +203,13 @@ function setProxy(options, configProxy, location, isRedirect) {
|
|
|
245
203
|
}
|
|
246
204
|
}
|
|
247
205
|
}
|
|
206
|
+
// Strip any tunneling agent we installed for the previous hop so a redirect
|
|
207
|
+
// that drops the proxy or crosses an HTTPS↔HTTP boundary doesn't reuse a
|
|
208
|
+
// stale one. Match on our Symbol marker so a user-supplied HttpsProxyAgent
|
|
209
|
+
// (which won't carry the marker) is left alone.
|
|
210
|
+
if (isRedirect && options.agent && options.agent[kAxiosInstalledTunnel]) {
|
|
211
|
+
options.agent = undefined;
|
|
212
|
+
}
|
|
248
213
|
if (proxy) {
|
|
249
214
|
// Read proxy fields without traversing the prototype chain. URL instances expose
|
|
250
215
|
// username/password/hostname/host/port/protocol via getters on URL.prototype (so
|
|
@@ -281,40 +246,96 @@ function setProxy(options, configProxy, location, isRedirect) {
|
|
|
281
246
|
} else if (authIsObject) {
|
|
282
247
|
throw new AxiosError('Invalid proxy authorization', AxiosError.ERR_BAD_OPTION, { proxy });
|
|
283
248
|
}
|
|
284
|
-
|
|
285
|
-
const base64 = Buffer.from(proxyAuth, 'utf8').toString('base64');
|
|
286
|
-
|
|
287
|
-
options.headers['Proxy-Authorization'] = 'Basic ' + base64;
|
|
288
249
|
}
|
|
289
250
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
251
|
+
const targetIsHttps = isHttps.test(options.protocol);
|
|
252
|
+
|
|
253
|
+
if (targetIsHttps) {
|
|
254
|
+
// CONNECT-tunneling path for HTTPS targets. Preserves end-to-end TLS to
|
|
255
|
+
// the origin so the proxy cannot inspect the URL, headers, or body — the
|
|
256
|
+
// behavior already promised by THREATMODEL.md (T-R9). HttpsProxyAgent
|
|
257
|
+
// sends Proxy-Authorization on the CONNECT request only, never on the
|
|
258
|
+
// wrapped TLS request, which is why we don't stamp it onto
|
|
259
|
+
// options.headers here. If the user already supplied an HttpsProxyAgent,
|
|
260
|
+
// they own tunneling end-to-end and we leave them alone; otherwise we
|
|
261
|
+
// install our own tunneling agent and forward their TLS options (if any)
|
|
262
|
+
// so a custom httpsAgent for cert pinning / rejectUnauthorized still
|
|
263
|
+
// applies to the origin TLS upgrade.
|
|
264
|
+
if (!(configHttpsAgent instanceof HttpsProxyAgent)) {
|
|
265
|
+
const proxyHost = readProxyField('hostname') || readProxyField('host');
|
|
266
|
+
const proxyPort = readProxyField('port');
|
|
267
|
+
const rawProxyProtocol = readProxyField('protocol');
|
|
268
|
+
const normalizedProtocol = rawProxyProtocol
|
|
269
|
+
? rawProxyProtocol.includes(':')
|
|
270
|
+
? rawProxyProtocol
|
|
271
|
+
: `${rawProxyProtocol}:`
|
|
272
|
+
: 'http:';
|
|
273
|
+
// Bracket IPv6 literals for URL parsing; URL.hostname strips the
|
|
274
|
+
// brackets again on read so the agent receives the raw form.
|
|
275
|
+
const proxyHostForURL =
|
|
276
|
+
proxyHost && proxyHost.includes(':') && !proxyHost.startsWith('[')
|
|
277
|
+
? `[${proxyHost}]`
|
|
278
|
+
: proxyHost;
|
|
279
|
+
const proxyURL = new URL(
|
|
280
|
+
`${normalizedProtocol}//${proxyHostForURL}${proxyPort ? ':' + proxyPort : ''}`
|
|
281
|
+
);
|
|
282
|
+
const agentOptions = {
|
|
283
|
+
protocol: proxyURL.protocol,
|
|
284
|
+
hostname: proxyURL.hostname.replace(/^\[|\]$/g, ''),
|
|
285
|
+
port: proxyURL.port,
|
|
286
|
+
auth: proxyAuth && typeof proxyAuth === 'string' ? proxyAuth : undefined,
|
|
287
|
+
};
|
|
288
|
+
if (proxyURL.protocol === 'https:') {
|
|
289
|
+
agentOptions.ALPNProtocols = ['http/1.1'];
|
|
290
|
+
}
|
|
291
|
+
const tunnelingAgent = getTunnelingAgent(agentOptions, configHttpsAgent);
|
|
292
|
+
// Set both: `options.agent` is consumed by the native https.request path
|
|
293
|
+
// (config.maxRedirects === 0); `options.agents.https` is consumed by
|
|
294
|
+
// follow-redirects, which ignores `options.agent` when `options.agents`
|
|
295
|
+
// is present.
|
|
296
|
+
options.agent = tunnelingAgent;
|
|
297
|
+
if (options.agents) {
|
|
298
|
+
options.agents.https = tunnelingAgent;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
// Forward-proxy mode for plaintext HTTP targets. The request line carries
|
|
303
|
+
// the absolute URL and the proxy sees everything — acceptable for plain
|
|
304
|
+
// HTTP since the wire was already plaintext.
|
|
305
|
+
if (proxyAuth) {
|
|
306
|
+
const base64 = Buffer.from(proxyAuth, 'utf8').toString('base64');
|
|
307
|
+
options.headers['Proxy-Authorization'] = 'Basic ' + base64;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Preserve a user-supplied Host header (case-insensitive) so callers can override
|
|
311
|
+
// the value forwarded to the proxy; otherwise default to the request URL's host.
|
|
312
|
+
let hasUserHostHeader = false;
|
|
313
|
+
for (const name of Object.keys(options.headers)) {
|
|
314
|
+
if (name.toLowerCase() === 'host') {
|
|
315
|
+
hasUserHostHeader = true;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (!hasUserHostHeader) {
|
|
320
|
+
options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
|
|
321
|
+
}
|
|
322
|
+
const proxyHost = readProxyField('hostname') || readProxyField('host');
|
|
323
|
+
options.hostname = proxyHost;
|
|
324
|
+
// Replace 'host' since options is not a URL object
|
|
325
|
+
options.host = proxyHost;
|
|
326
|
+
options.port = readProxyField('port');
|
|
327
|
+
options.path = location;
|
|
328
|
+
const proxyProtocol = readProxyField('protocol');
|
|
329
|
+
if (proxyProtocol) {
|
|
330
|
+
options.protocol = proxyProtocol.includes(':') ? proxyProtocol : `${proxyProtocol}:`;
|
|
297
331
|
}
|
|
298
|
-
}
|
|
299
|
-
if (!hasUserHostHeader) {
|
|
300
|
-
options.headers.host = options.hostname + (options.port ? ':' + options.port : '');
|
|
301
|
-
}
|
|
302
|
-
const proxyHost = readProxyField('hostname') || readProxyField('host');
|
|
303
|
-
options.hostname = proxyHost;
|
|
304
|
-
// Replace 'host' since options is not a URL object
|
|
305
|
-
options.host = proxyHost;
|
|
306
|
-
options.port = readProxyField('port');
|
|
307
|
-
options.path = location;
|
|
308
|
-
const proxyProtocol = readProxyField('protocol');
|
|
309
|
-
if (proxyProtocol) {
|
|
310
|
-
options.protocol = proxyProtocol.includes(':') ? proxyProtocol : `${proxyProtocol}:`;
|
|
311
332
|
}
|
|
312
333
|
}
|
|
313
334
|
|
|
314
335
|
options.beforeRedirects.proxy = function beforeRedirect(redirectOptions) {
|
|
315
336
|
// Configure proxy for redirected request, passing the original config proxy to apply
|
|
316
337
|
// the exact same logic as if the redirected request was performed by axios directly.
|
|
317
|
-
setProxy(redirectOptions, configProxy, redirectOptions.href, true);
|
|
338
|
+
setProxy(redirectOptions, configProxy, redirectOptions.href, true, configHttpsAgent);
|
|
318
339
|
};
|
|
319
340
|
}
|
|
320
341
|
|
|
@@ -414,6 +435,7 @@ export default isHttpAdapterSupported &&
|
|
|
414
435
|
function httpAdapter(config) {
|
|
415
436
|
return wrapAsync(async function dispatchHttpRequest(resolve, reject, onDone) {
|
|
416
437
|
const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined);
|
|
438
|
+
const transitional = own('transitional') || transitionalDefaults;
|
|
417
439
|
let data = own('data');
|
|
418
440
|
let lookup = own('lookup');
|
|
419
441
|
let family = own('family');
|
|
@@ -467,7 +489,7 @@ export default isHttpAdapterSupported &&
|
|
|
467
489
|
!reason || reason.type ? new CanceledError(null, config, req) : reason
|
|
468
490
|
);
|
|
469
491
|
} catch (err) {
|
|
470
|
-
|
|
492
|
+
// ignore emit errors
|
|
471
493
|
}
|
|
472
494
|
}
|
|
473
495
|
|
|
@@ -482,7 +504,6 @@ export default isHttpAdapterSupported &&
|
|
|
482
504
|
let timeoutErrorMessage = config.timeout
|
|
483
505
|
? 'timeout of ' + config.timeout + 'ms exceeded'
|
|
484
506
|
: 'timeout exceeded';
|
|
485
|
-
const transitional = config.transitional || transitionalDefaults;
|
|
486
507
|
if (config.timeoutErrorMessage) {
|
|
487
508
|
timeoutErrorMessage = config.timeoutErrorMessage;
|
|
488
509
|
}
|
|
@@ -731,7 +752,7 @@ export default isHttpAdapterSupported &&
|
|
|
731
752
|
auth = username + ':' + password;
|
|
732
753
|
}
|
|
733
754
|
|
|
734
|
-
if (!auth && parsed.username) {
|
|
755
|
+
if (!auth && (parsed.username || parsed.password)) {
|
|
735
756
|
const urlUsername = decodeURIComponentSafe(parsed.username);
|
|
736
757
|
const urlPassword = decodeURIComponentSafe(parsed.password);
|
|
737
758
|
auth = urlUsername + ':' + urlPassword;
|
|
@@ -757,7 +778,8 @@ export default isHttpAdapterSupported &&
|
|
|
757
778
|
|
|
758
779
|
headers.set(
|
|
759
780
|
'Accept-Encoding',
|
|
760
|
-
|
|
781
|
+
utils.hasOwnProp(transitional, 'advertiseZstdAcceptEncoding') &&
|
|
782
|
+
transitional.advertiseZstdAcceptEncoding === true ? ACCEPT_ENCODING_WITH_ZSTD : ACCEPT_ENCODING,
|
|
761
783
|
false
|
|
762
784
|
);
|
|
763
785
|
|
|
@@ -766,7 +788,7 @@ export default isHttpAdapterSupported &&
|
|
|
766
788
|
const options = Object.assign(Object.create(null), {
|
|
767
789
|
path,
|
|
768
790
|
method: method,
|
|
769
|
-
headers: headers
|
|
791
|
+
headers: toByteStringHeaderObject(headers),
|
|
770
792
|
agents: { http: config.httpAgent, https: config.httpsAgent },
|
|
771
793
|
auth,
|
|
772
794
|
protocol,
|
|
@@ -779,19 +801,21 @@ export default isHttpAdapterSupported &&
|
|
|
779
801
|
// cacheable-lookup integration hotfix
|
|
780
802
|
!utils.isUndefined(lookup) && (options.lookup = lookup);
|
|
781
803
|
|
|
782
|
-
|
|
783
|
-
|
|
804
|
+
const socketPath = own('socketPath');
|
|
805
|
+
if (socketPath) {
|
|
806
|
+
if (typeof socketPath !== 'string') {
|
|
784
807
|
return reject(
|
|
785
808
|
new AxiosError('socketPath must be a string', AxiosError.ERR_BAD_OPTION_VALUE, config)
|
|
786
809
|
);
|
|
787
810
|
}
|
|
788
811
|
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
812
|
+
const allowedSocketPaths = own('allowedSocketPaths');
|
|
813
|
+
if (allowedSocketPaths != null) {
|
|
814
|
+
const allowed = Array.isArray(allowedSocketPaths)
|
|
815
|
+
? allowedSocketPaths
|
|
816
|
+
: [allowedSocketPaths];
|
|
793
817
|
|
|
794
|
-
const resolvedSocket = resolvePath(
|
|
818
|
+
const resolvedSocket = resolvePath(socketPath);
|
|
795
819
|
const isAllowed = allowed.some(
|
|
796
820
|
(entry) => typeof entry === 'string' && resolvePath(entry) === resolvedSocket
|
|
797
821
|
);
|
|
@@ -799,7 +823,7 @@ export default isHttpAdapterSupported &&
|
|
|
799
823
|
if (!isAllowed) {
|
|
800
824
|
return reject(
|
|
801
825
|
new AxiosError(
|
|
802
|
-
`socketPath "${
|
|
826
|
+
`socketPath "${socketPath}" is not permitted by allowedSocketPaths`,
|
|
803
827
|
AxiosError.ERR_BAD_OPTION_VALUE,
|
|
804
828
|
config
|
|
805
829
|
)
|
|
@@ -807,7 +831,7 @@ export default isHttpAdapterSupported &&
|
|
|
807
831
|
}
|
|
808
832
|
}
|
|
809
833
|
|
|
810
|
-
options.socketPath =
|
|
834
|
+
options.socketPath = socketPath;
|
|
811
835
|
} else {
|
|
812
836
|
options.hostname = parsed.hostname.startsWith('[')
|
|
813
837
|
? parsed.hostname.slice(1, -1)
|
|
@@ -816,13 +840,19 @@ export default isHttpAdapterSupported &&
|
|
|
816
840
|
setProxy(
|
|
817
841
|
options,
|
|
818
842
|
config.proxy,
|
|
819
|
-
protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path
|
|
843
|
+
protocol + '//' + parsed.hostname + (parsed.port ? ':' + parsed.port : '') + options.path,
|
|
844
|
+
false,
|
|
845
|
+
config.httpsAgent
|
|
820
846
|
);
|
|
821
847
|
}
|
|
822
848
|
let transport;
|
|
823
849
|
let isNativeTransport = false;
|
|
824
850
|
const isHttpsRequest = isHttps.test(options.protocol);
|
|
825
|
-
|
|
851
|
+
// Don't clobber a CONNECT-tunneling agent installed by setProxy() for an
|
|
852
|
+
// HTTPS target.
|
|
853
|
+
if (options.agent == null) {
|
|
854
|
+
options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
|
|
855
|
+
}
|
|
826
856
|
|
|
827
857
|
if (isHttp2) {
|
|
828
858
|
transport = http2Transport;
|
|
@@ -841,6 +871,23 @@ export default isHttpAdapterSupported &&
|
|
|
841
871
|
if (configBeforeRedirect) {
|
|
842
872
|
options.beforeRedirects.config = configBeforeRedirect;
|
|
843
873
|
}
|
|
874
|
+
if (auth) {
|
|
875
|
+
// Restore HTTP Basic credentials on same-origin redirects only.
|
|
876
|
+
// follow-redirects >= 1.15.8 strips Authorization on every redirect (see #6929);
|
|
877
|
+
// cross-origin stripping is the documented mitigation for T-R2 in THREATMODEL.md
|
|
878
|
+
// and is preserved by deliberately not restoring on origin change.
|
|
879
|
+
const requestOrigin = parsed.origin;
|
|
880
|
+
const authToRestore = auth;
|
|
881
|
+
options.beforeRedirects.auth = function beforeRedirectAuth(redirectOptions) {
|
|
882
|
+
try {
|
|
883
|
+
if (new URL(redirectOptions.href).origin === requestOrigin) {
|
|
884
|
+
redirectOptions.auth = authToRestore;
|
|
885
|
+
}
|
|
886
|
+
} catch (e) {
|
|
887
|
+
// ignore malformed URL: leaving auth stripped is fail-safe
|
|
888
|
+
}
|
|
889
|
+
};
|
|
890
|
+
}
|
|
844
891
|
transport = isHttpsRequest ? httpsFollow : httpFollow;
|
|
845
892
|
}
|
|
846
893
|
}
|
|
@@ -927,6 +974,13 @@ export default isHttpAdapterSupported &&
|
|
|
927
974
|
streams.push(zlib.createBrotliDecompress(brotliOptions));
|
|
928
975
|
delete res.headers['content-encoding'];
|
|
929
976
|
}
|
|
977
|
+
break;
|
|
978
|
+
case 'zstd':
|
|
979
|
+
if (isZstdSupported) {
|
|
980
|
+
streams.push(zlib.createZstdDecompress(zstdOptions));
|
|
981
|
+
delete res.headers['content-encoding'];
|
|
982
|
+
}
|
|
983
|
+
break;
|
|
930
984
|
}
|
|
931
985
|
}
|
|
932
986
|
|
package/lib/adapters/xhr.js
CHANGED
|
@@ -8,6 +8,7 @@ import platform from '../platform/index.js';
|
|
|
8
8
|
import AxiosHeaders from '../core/AxiosHeaders.js';
|
|
9
9
|
import { progressEventReducer } from '../helpers/progressEventReducer.js';
|
|
10
10
|
import resolveConfig from '../helpers/resolveConfig.js';
|
|
11
|
+
import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js';
|
|
11
12
|
|
|
12
13
|
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
|
|
13
14
|
|
|
@@ -156,7 +157,7 @@ export default isXHRAdapterSupported &&
|
|
|
156
157
|
|
|
157
158
|
// Add headers to the request
|
|
158
159
|
if ('setRequestHeader' in request) {
|
|
159
|
-
utils.forEach(requestHeaders
|
|
160
|
+
utils.forEach(toByteStringHeaderObject(requestHeaders), function setRequestHeader(val, key) {
|
|
160
161
|
request.setRequestHeader(key, val);
|
|
161
162
|
});
|
|
162
163
|
}
|
package/lib/core/Axios.js
CHANGED
|
@@ -101,6 +101,7 @@ 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),
|
|
104
105
|
},
|
|
105
106
|
false
|
|
106
107
|
);
|
package/lib/core/AxiosHeaders.js
CHANGED
|
@@ -2,46 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import utils from '../utils.js';
|
|
4
4
|
import parseHeaders from '../helpers/parseHeaders.js';
|
|
5
|
+
import { sanitizeHeaderValue } from '../helpers/sanitizeHeaderValue.js';
|
|
5
6
|
|
|
6
7
|
const $internals = Symbol('internals');
|
|
7
8
|
|
|
8
|
-
const INVALID_HEADER_VALUE_CHARS_RE = /[^\x09\x20-\x7E\x80-\xFF]/g;
|
|
9
|
-
|
|
10
|
-
function trimSPorHTAB(str) {
|
|
11
|
-
let start = 0;
|
|
12
|
-
let end = str.length;
|
|
13
|
-
|
|
14
|
-
while (start < end) {
|
|
15
|
-
const code = str.charCodeAt(start);
|
|
16
|
-
|
|
17
|
-
if (code !== 0x09 && code !== 0x20) {
|
|
18
|
-
break;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
start += 1;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
while (end > start) {
|
|
25
|
-
const code = str.charCodeAt(end - 1);
|
|
26
|
-
|
|
27
|
-
if (code !== 0x09 && code !== 0x20) {
|
|
28
|
-
break;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
end -= 1;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return start === 0 && end === str.length ? str : str.slice(start, end);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
9
|
function normalizeHeader(header) {
|
|
38
10
|
return header && String(header).trim().toLowerCase();
|
|
39
11
|
}
|
|
40
12
|
|
|
41
|
-
function sanitizeHeaderValue(str) {
|
|
42
|
-
return trimSPorHTAB(str.replace(INVALID_HEADER_VALUE_CHARS_RE, ''));
|
|
43
|
-
}
|
|
44
|
-
|
|
45
13
|
function normalizeValue(value) {
|
|
46
14
|
if (value === false || value == null) {
|
|
47
15
|
return value;
|
|
@@ -121,7 +89,7 @@ class AxiosHeaders {
|
|
|
121
89
|
const lHeader = normalizeHeader(_header);
|
|
122
90
|
|
|
123
91
|
if (!lHeader) {
|
|
124
|
-
|
|
92
|
+
return;
|
|
125
93
|
}
|
|
126
94
|
|
|
127
95
|
const key = utils.findKey(self, lHeader);
|
|
@@ -149,7 +117,7 @@ class AxiosHeaders {
|
|
|
149
117
|
key;
|
|
150
118
|
for (const entry of header) {
|
|
151
119
|
if (!utils.isArray(entry)) {
|
|
152
|
-
throw TypeError('Object iterator must return a key-value pair');
|
|
120
|
+
throw new TypeError('Object iterator must return a key-value pair');
|
|
153
121
|
}
|
|
154
122
|
|
|
155
123
|
obj[(key = entry[0])] = (dest = obj[key])
|
package/lib/env/data.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "1.
|
|
1
|
+
export const VERSION = "1.17.0";
|