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.
@@ -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 with the options
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
- // Preserve a user-supplied Host header (case-insensitive) so callers can override
291
- // the value forwarded to the proxy; otherwise default to the request URL's host.
292
- let hasUserHostHeader = false;
293
- for (const name of Object.keys(options.headers)) {
294
- if (name.toLowerCase() === 'host') {
295
- hasUserHostHeader = true;
296
- break;
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
- console.warn('emit error', err);
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
- 'gzip, compress, deflate' + (isBrotliSupported ? ', br' : ''),
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.toJSON(),
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
- if (config.socketPath) {
783
- if (typeof config.socketPath !== 'string') {
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
- if (config.allowedSocketPaths != null) {
790
- const allowed = Array.isArray(config.allowedSocketPaths)
791
- ? config.allowedSocketPaths
792
- : [config.allowedSocketPaths];
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(config.socketPath);
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 "${config.socketPath}" is not permitted by allowedSocketPaths`,
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 = config.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
- options.agent = isHttpsRequest ? config.httpsAgent : config.httpAgent;
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
 
@@ -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.toJSON(), function setRequestHeader(val, key) {
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
  );
@@ -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
- throw new Error('header name must be a non-empty string');
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])
@@ -5,4 +5,5 @@ export default {
5
5
  forcedJSONParsing: true,
6
6
  clarifyTimeoutError: false,
7
7
  legacyInterceptorReqResOrdering: true,
8
+ advertiseZstdAcceptEncoding: false,
8
9
  };
package/lib/env/data.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "1.16.0";
1
+ export const VERSION = "1.17.0";