axios 1.15.2 → 1.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -6
- package/README.md +396 -25
- package/dist/axios.js +1455 -1109
- package/dist/axios.js.map +1 -1
- package/dist/axios.min.js +3 -3
- package/dist/axios.min.js.map +1 -1
- package/dist/browser/axios.cjs +1569 -1174
- package/dist/browser/axios.cjs.map +1 -1
- package/dist/esm/axios.js +1569 -1173
- package/dist/esm/axios.js.map +1 -1
- package/dist/esm/axios.min.js +2 -2
- package/dist/esm/axios.min.js.map +1 -1
- package/dist/node/axios.cjs +1395 -915
- package/dist/node/axios.cjs.map +1 -1
- package/index.d.cts +25 -13
- package/index.d.ts +21 -4
- package/index.js +2 -0
- package/lib/adapters/adapters.js +4 -2
- package/lib/adapters/fetch.js +131 -11
- package/lib/adapters/http.js +298 -69
- package/lib/adapters/xhr.js +8 -3
- package/lib/core/Axios.js +7 -3
- package/lib/core/AxiosError.js +86 -1
- package/lib/core/AxiosHeaders.js +4 -33
- package/lib/core/dispatchRequest.js +19 -7
- package/lib/core/mergeConfig.js +6 -3
- package/lib/core/settle.js +7 -11
- package/lib/defaults/index.js +1 -1
- package/lib/env/data.js +1 -1
- package/lib/helpers/buildURL.js +1 -1
- package/lib/helpers/composeSignals.js +48 -47
- package/lib/helpers/cookies.js +14 -2
- package/lib/helpers/estimateDataURLDecodedBytes.js +28 -1
- package/lib/helpers/formDataToJSON.js +1 -1
- package/lib/helpers/formDataToStream.js +1 -1
- package/lib/helpers/fromDataURI.js +18 -5
- package/lib/helpers/parseProtocol.js +1 -1
- package/lib/helpers/progressEventReducer.js +3 -0
- package/lib/helpers/resolveConfig.js +33 -17
- package/lib/helpers/sanitizeHeaderValue.js +60 -0
- package/lib/helpers/shouldBypassProxy.js +26 -1
- package/lib/helpers/validator.js +1 -1
- package/lib/utils.js +35 -22
- package/package.json +19 -24
package/lib/core/AxiosError.js
CHANGED
|
@@ -1,6 +1,76 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
import utils from '../utils.js';
|
|
4
|
+
import AxiosHeaders from './AxiosHeaders.js';
|
|
5
|
+
|
|
6
|
+
const REDACTED = '[REDACTED ****]';
|
|
7
|
+
|
|
8
|
+
function hasOwnOrPrototypeToJSON(source) {
|
|
9
|
+
if (utils.hasOwnProp(source, 'toJSON')) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let prototype = Object.getPrototypeOf(source);
|
|
14
|
+
|
|
15
|
+
while (prototype && prototype !== Object.prototype) {
|
|
16
|
+
if (utils.hasOwnProp(prototype, 'toJSON')) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
prototype = Object.getPrototypeOf(prototype);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Build a plain-object snapshot of `config` and replace the value of any key
|
|
27
|
+
// (case-insensitive) listed in `redactKeys` with REDACTED. Walks through arrays
|
|
28
|
+
// and AxiosHeaders, and short-circuits on circular references.
|
|
29
|
+
function redactConfig(config, redactKeys) {
|
|
30
|
+
const lowerKeys = new Set(redactKeys.map((k) => String(k).toLowerCase()));
|
|
31
|
+
const seen = [];
|
|
32
|
+
|
|
33
|
+
const visit = (source) => {
|
|
34
|
+
if (source === null || typeof source !== 'object') return source;
|
|
35
|
+
if (utils.isBuffer(source)) return source;
|
|
36
|
+
if (seen.indexOf(source) !== -1) return undefined;
|
|
37
|
+
|
|
38
|
+
if (source instanceof AxiosHeaders) {
|
|
39
|
+
source = source.toJSON();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
seen.push(source);
|
|
43
|
+
|
|
44
|
+
let result;
|
|
45
|
+
if (utils.isArray(source)) {
|
|
46
|
+
result = [];
|
|
47
|
+
source.forEach((v, i) => {
|
|
48
|
+
const reducedValue = visit(v);
|
|
49
|
+
if (!utils.isUndefined(reducedValue)) {
|
|
50
|
+
result[i] = reducedValue;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
if (!utils.isPlainObject(source) && hasOwnOrPrototypeToJSON(source)) {
|
|
55
|
+
seen.pop();
|
|
56
|
+
return source;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
result = Object.create(null);
|
|
60
|
+
for (const [key, value] of Object.entries(source)) {
|
|
61
|
+
const reducedValue = lowerKeys.has(key.toLowerCase()) ? REDACTED : visit(value);
|
|
62
|
+
if (!utils.isUndefined(reducedValue)) {
|
|
63
|
+
result[key] = reducedValue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
seen.pop();
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return visit(config);
|
|
73
|
+
}
|
|
4
74
|
|
|
5
75
|
class AxiosError extends Error {
|
|
6
76
|
static from(error, code, config, request, response, customProps) {
|
|
@@ -35,6 +105,9 @@ class AxiosError extends Error {
|
|
|
35
105
|
// The native Error constructor sets message as non-enumerable,
|
|
36
106
|
// but axios < v1.13.3 had it as enumerable
|
|
37
107
|
Object.defineProperty(this, 'message', {
|
|
108
|
+
// Null-proto descriptor so a polluted Object.prototype.get cannot turn
|
|
109
|
+
// this data descriptor into an accessor descriptor on the way in.
|
|
110
|
+
__proto__: null,
|
|
38
111
|
value: message,
|
|
39
112
|
enumerable: true,
|
|
40
113
|
writable: true,
|
|
@@ -53,6 +126,17 @@ class AxiosError extends Error {
|
|
|
53
126
|
}
|
|
54
127
|
|
|
55
128
|
toJSON() {
|
|
129
|
+
// Opt-in redaction: when the request config carries a `redact` array, the
|
|
130
|
+
// value of any matching key (case-insensitive, at any depth) is replaced
|
|
131
|
+
// with REDACTED in the serialized snapshot. Undefined or empty leaves the
|
|
132
|
+
// existing serialization behavior unchanged.
|
|
133
|
+
const config = this.config;
|
|
134
|
+
const redactKeys = config && utils.hasOwnProp(config, 'redact') ? config.redact : undefined;
|
|
135
|
+
const serializedConfig =
|
|
136
|
+
utils.isArray(redactKeys) && redactKeys.length > 0
|
|
137
|
+
? redactConfig(config, redactKeys)
|
|
138
|
+
: utils.toJSONObject(config);
|
|
139
|
+
|
|
56
140
|
return {
|
|
57
141
|
// Standard
|
|
58
142
|
message: this.message,
|
|
@@ -66,7 +150,7 @@ class AxiosError extends Error {
|
|
|
66
150
|
columnNumber: this.columnNumber,
|
|
67
151
|
stack: this.stack,
|
|
68
152
|
// Axios
|
|
69
|
-
config:
|
|
153
|
+
config: serializedConfig,
|
|
70
154
|
code: this.code,
|
|
71
155
|
status: this.status,
|
|
72
156
|
};
|
|
@@ -78,6 +162,7 @@ AxiosError.ERR_BAD_OPTION_VALUE = 'ERR_BAD_OPTION_VALUE';
|
|
|
78
162
|
AxiosError.ERR_BAD_OPTION = 'ERR_BAD_OPTION';
|
|
79
163
|
AxiosError.ECONNABORTED = 'ECONNABORTED';
|
|
80
164
|
AxiosError.ETIMEDOUT = 'ETIMEDOUT';
|
|
165
|
+
AxiosError.ECONNREFUSED = 'ECONNREFUSED';
|
|
81
166
|
AxiosError.ERR_NETWORK = 'ERR_NETWORK';
|
|
82
167
|
AxiosError.ERR_FR_TOO_MANY_REDIRECTS = 'ERR_FR_TOO_MANY_REDIRECTS';
|
|
83
168
|
AxiosError.ERR_DEPRECATED = 'ERR_DEPRECATED';
|
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;
|
|
@@ -98,6 +66,9 @@ function buildAccessors(obj, header) {
|
|
|
98
66
|
|
|
99
67
|
['get', 'set', 'has'].forEach((methodName) => {
|
|
100
68
|
Object.defineProperty(obj, methodName + accessorName, {
|
|
69
|
+
// Null-proto descriptor so a polluted Object.prototype.get cannot turn
|
|
70
|
+
// this data descriptor into an accessor descriptor on the way in.
|
|
71
|
+
__proto__: null,
|
|
101
72
|
value: function (arg1, arg2, arg3) {
|
|
102
73
|
return this[methodName].call(this, header, arg1, arg2, arg3);
|
|
103
74
|
},
|
|
@@ -49,8 +49,15 @@ export default function dispatchRequest(config) {
|
|
|
49
49
|
function onAdapterResolution(response) {
|
|
50
50
|
throwIfCancellationRequested(config);
|
|
51
51
|
|
|
52
|
-
//
|
|
53
|
-
|
|
52
|
+
// Expose the current response on config so that transformResponse can
|
|
53
|
+
// attach it to any AxiosError it throws (e.g. on JSON parse failure).
|
|
54
|
+
// We clean it up afterwards to avoid polluting the config object.
|
|
55
|
+
config.response = response;
|
|
56
|
+
try {
|
|
57
|
+
response.data = transformData.call(config, config.transformResponse, response);
|
|
58
|
+
} finally {
|
|
59
|
+
delete config.response;
|
|
60
|
+
}
|
|
54
61
|
|
|
55
62
|
response.headers = AxiosHeaders.from(response.headers);
|
|
56
63
|
|
|
@@ -62,11 +69,16 @@ export default function dispatchRequest(config) {
|
|
|
62
69
|
|
|
63
70
|
// Transform response data
|
|
64
71
|
if (reason && reason.response) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
config.response = reason.response;
|
|
73
|
+
try {
|
|
74
|
+
reason.response.data = transformData.call(
|
|
75
|
+
config,
|
|
76
|
+
config.transformResponse,
|
|
77
|
+
reason.response
|
|
78
|
+
);
|
|
79
|
+
} finally {
|
|
80
|
+
delete config.response;
|
|
81
|
+
}
|
|
70
82
|
reason.response.headers = AxiosHeaders.from(reason.response.headers);
|
|
71
83
|
}
|
|
72
84
|
}
|
package/lib/core/mergeConfig.js
CHANGED
|
@@ -19,11 +19,14 @@ export default function mergeConfig(config1, config2) {
|
|
|
19
19
|
config2 = config2 || {};
|
|
20
20
|
|
|
21
21
|
// Use a null-prototype object so that downstream reads such as `config.auth`
|
|
22
|
-
// or `config.baseURL` cannot inherit polluted values from Object.prototype
|
|
23
|
-
//
|
|
24
|
-
//
|
|
22
|
+
// or `config.baseURL` cannot inherit polluted values from Object.prototype.
|
|
23
|
+
// `hasOwnProperty` is restored as a non-enumerable own slot to preserve
|
|
24
|
+
// ergonomics for user code that relies on it.
|
|
25
25
|
const config = Object.create(null);
|
|
26
26
|
Object.defineProperty(config, 'hasOwnProperty', {
|
|
27
|
+
// Null-proto descriptor so a polluted Object.prototype.get cannot turn
|
|
28
|
+
// this data descriptor into an accessor descriptor on the way in.
|
|
29
|
+
__proto__: null,
|
|
27
30
|
value: Object.prototype.hasOwnProperty,
|
|
28
31
|
enumerable: false,
|
|
29
32
|
writable: true,
|
package/lib/core/settle.js
CHANGED
|
@@ -16,16 +16,12 @@ export default function settle(resolve, reject, response) {
|
|
|
16
16
|
if (!response.status || !validateStatus || validateStatus(response.status)) {
|
|
17
17
|
resolve(response);
|
|
18
18
|
} else {
|
|
19
|
-
reject(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
response.request,
|
|
27
|
-
response
|
|
28
|
-
)
|
|
29
|
-
);
|
|
19
|
+
reject(new AxiosError(
|
|
20
|
+
'Request failed with status code ' + response.status,
|
|
21
|
+
response.status >= 400 && response.status < 500 ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE,
|
|
22
|
+
response.config,
|
|
23
|
+
response.request,
|
|
24
|
+
response
|
|
25
|
+
));
|
|
30
26
|
}
|
|
31
27
|
}
|
package/lib/defaults/index.js
CHANGED
|
@@ -170,7 +170,7 @@ const defaults = {
|
|
|
170
170
|
},
|
|
171
171
|
};
|
|
172
172
|
|
|
173
|
-
utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch'], (method) => {
|
|
173
|
+
utils.forEach(['delete', 'get', 'head', 'post', 'put', 'patch', 'query'], (method) => {
|
|
174
174
|
defaults.headers[method] = {};
|
|
175
175
|
});
|
|
176
176
|
|
package/lib/env/data.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "1.
|
|
1
|
+
export const VERSION = "1.16.1";
|
package/lib/helpers/buildURL.js
CHANGED
|
@@ -11,7 +11,7 @@ import AxiosURLSearchParams from '../helpers/AxiosURLSearchParams.js';
|
|
|
11
11
|
*
|
|
12
12
|
* @returns {string} The encoded value.
|
|
13
13
|
*/
|
|
14
|
-
function encode(val) {
|
|
14
|
+
export function encode(val) {
|
|
15
15
|
return encodeURIComponent(val)
|
|
16
16
|
.replace(/%3A/gi, ':')
|
|
17
17
|
.replace(/%24/g, '$')
|
|
@@ -3,54 +3,55 @@ import AxiosError from '../core/AxiosError.js';
|
|
|
3
3
|
import utils from '../utils.js';
|
|
4
4
|
|
|
5
5
|
const composeSignals = (signals, timeout) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (timeout
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
let aborted;
|
|
12
|
-
|
|
13
|
-
const onabort = function (reason) {
|
|
14
|
-
if (!aborted) {
|
|
15
|
-
aborted = true;
|
|
16
|
-
unsubscribe();
|
|
17
|
-
const err = reason instanceof Error ? reason : this.reason;
|
|
18
|
-
controller.abort(
|
|
19
|
-
err instanceof AxiosError
|
|
20
|
-
? err
|
|
21
|
-
: new CanceledError(err instanceof Error ? err.message : err)
|
|
22
|
-
);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
let timer =
|
|
27
|
-
timeout &&
|
|
28
|
-
setTimeout(() => {
|
|
29
|
-
timer = null;
|
|
30
|
-
onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT));
|
|
31
|
-
}, timeout);
|
|
32
|
-
|
|
33
|
-
const unsubscribe = () => {
|
|
34
|
-
if (signals) {
|
|
35
|
-
timer && clearTimeout(timer);
|
|
36
|
-
timer = null;
|
|
37
|
-
signals.forEach((signal) => {
|
|
38
|
-
signal.unsubscribe
|
|
39
|
-
? signal.unsubscribe(onabort)
|
|
40
|
-
: signal.removeEventListener('abort', onabort);
|
|
41
|
-
});
|
|
42
|
-
signals = null;
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
signals.forEach((signal) => signal.addEventListener('abort', onabort));
|
|
47
|
-
|
|
48
|
-
const { signal } = controller;
|
|
49
|
-
|
|
50
|
-
signal.unsubscribe = () => utils.asap(unsubscribe);
|
|
51
|
-
|
|
52
|
-
return signal;
|
|
6
|
+
signals = signals ? signals.filter(Boolean) : [];
|
|
7
|
+
|
|
8
|
+
if (!timeout && !signals.length) {
|
|
9
|
+
return;
|
|
53
10
|
}
|
|
11
|
+
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
|
|
14
|
+
let aborted = false;
|
|
15
|
+
|
|
16
|
+
const onabort = function (reason) {
|
|
17
|
+
if (!aborted) {
|
|
18
|
+
aborted = true;
|
|
19
|
+
unsubscribe();
|
|
20
|
+
const err = reason instanceof Error ? reason : this.reason;
|
|
21
|
+
controller.abort(
|
|
22
|
+
err instanceof AxiosError
|
|
23
|
+
? err
|
|
24
|
+
: new CanceledError(err instanceof Error ? err.message : err)
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let timer =
|
|
30
|
+
timeout &&
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
timer = null;
|
|
33
|
+
onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT));
|
|
34
|
+
}, timeout);
|
|
35
|
+
|
|
36
|
+
const unsubscribe = () => {
|
|
37
|
+
if (!signals) { return; }
|
|
38
|
+
timer && clearTimeout(timer);
|
|
39
|
+
timer = null;
|
|
40
|
+
signals.forEach((signal) => {
|
|
41
|
+
signal.unsubscribe
|
|
42
|
+
? signal.unsubscribe(onabort)
|
|
43
|
+
: signal.removeEventListener('abort', onabort);
|
|
44
|
+
});
|
|
45
|
+
signals = null;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
signals.forEach((signal) => signal.addEventListener('abort', onabort));
|
|
49
|
+
|
|
50
|
+
const { signal } = controller;
|
|
51
|
+
|
|
52
|
+
signal.unsubscribe = () => utils.asap(unsubscribe);
|
|
53
|
+
|
|
54
|
+
return signal;
|
|
54
55
|
};
|
|
55
56
|
|
|
56
57
|
export default composeSignals;
|
package/lib/helpers/cookies.js
CHANGED
|
@@ -30,8 +30,20 @@ export default platform.hasStandardBrowserEnv
|
|
|
30
30
|
|
|
31
31
|
read(name) {
|
|
32
32
|
if (typeof document === 'undefined') return null;
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
// Match name=value by splitting on the semicolon separator instead of building a
|
|
34
|
+
// RegExp from `name` — interpolating an unescaped string into a RegExp would let
|
|
35
|
+
// metacharacters (e.g. `.+?` in an attacker-influenced cookie name) cause ReDoS or
|
|
36
|
+
// match the wrong cookie. Browsers may serialize cookie pairs as either ";" or
|
|
37
|
+
// "; ", so ignore optional whitespace before each cookie name.
|
|
38
|
+
const cookies = document.cookie.split(';');
|
|
39
|
+
for (let i = 0; i < cookies.length; i++) {
|
|
40
|
+
const cookie = cookies[i].replace(/^\s+/, '');
|
|
41
|
+
const eq = cookie.indexOf('=');
|
|
42
|
+
if (eq !== -1 && cookie.slice(0, eq) === name) {
|
|
43
|
+
return decodeURIComponent(cookie.slice(eq + 1));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
35
47
|
},
|
|
36
48
|
|
|
37
49
|
remove(name) {
|
|
@@ -69,5 +69,32 @@ export default function estimateDataURLDecodedBytes(url) {
|
|
|
69
69
|
return bytes > 0 ? bytes : 0;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
if (typeof Buffer !== 'undefined' && typeof Buffer.byteLength === 'function') {
|
|
73
|
+
return Buffer.byteLength(body, 'utf8');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Compute UTF-8 byte length directly from UTF-16 code units without allocating
|
|
77
|
+
// a byte buffer (TextEncoder.encode would defeat the DoS guard on large bodies).
|
|
78
|
+
// Using body.length here would undercount non-ASCII (e.g. '€' is 1 code unit
|
|
79
|
+
// but 3 UTF-8 bytes).
|
|
80
|
+
let bytes = 0;
|
|
81
|
+
for (let i = 0, len = body.length; i < len; i++) {
|
|
82
|
+
const c = body.charCodeAt(i);
|
|
83
|
+
if (c < 0x80) {
|
|
84
|
+
bytes += 1;
|
|
85
|
+
} else if (c < 0x800) {
|
|
86
|
+
bytes += 2;
|
|
87
|
+
} else if (c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
|
|
88
|
+
const next = body.charCodeAt(i + 1);
|
|
89
|
+
if (next >= 0xdc00 && next <= 0xdfff) {
|
|
90
|
+
bytes += 4;
|
|
91
|
+
i++;
|
|
92
|
+
} else {
|
|
93
|
+
bytes += 3;
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
bytes += 3;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return bytes;
|
|
73
100
|
}
|
|
@@ -77,7 +77,7 @@ const formDataToStream = (form, headersHandler, options) => {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if (boundary.length < 1 || boundary.length > 70) {
|
|
80
|
-
throw Error('boundary must be
|
|
80
|
+
throw Error('boundary must be 1-70 characters long');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
const boundaryBytes = textEncoder.encode('--' + boundary + CRLF);
|
|
@@ -4,7 +4,9 @@ import AxiosError from '../core/AxiosError.js';
|
|
|
4
4
|
import parseProtocol from './parseProtocol.js';
|
|
5
5
|
import platform from '../platform/index.js';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// RFC 2397: data:[<mediatype>][;base64],<data>
|
|
8
|
+
// mediatype = type/subtype followed by optional ;name=value parameters
|
|
9
|
+
const DATA_URL_PATTERN = /^([^,;]+\/[^,;]+)?((?:;[^,;=]+=[^,;]+)*)(;base64)?,([\s\S]*)$/;
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Parse data uri to a Buffer or Blob
|
|
@@ -33,10 +35,21 @@ export default function fromDataURI(uri, asBlob, options) {
|
|
|
33
35
|
throw new AxiosError('Invalid URL', AxiosError.ERR_INVALID_URL);
|
|
34
36
|
}
|
|
35
37
|
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
38
|
+
const type = match[1];
|
|
39
|
+
const params = match[2];
|
|
40
|
+
const encoding = match[3] ? 'base64' : 'utf8';
|
|
41
|
+
const body = match[4];
|
|
42
|
+
|
|
43
|
+
// RFC 2397 section 3: default mediatype is text/plain;charset=US-ASCII
|
|
44
|
+
// Bare `data:,` leaves mime undefined; Blob normalises that to "" per spec.
|
|
45
|
+
let mime;
|
|
46
|
+
if (type) {
|
|
47
|
+
mime = params ? type + params : type;
|
|
48
|
+
} else if (params) {
|
|
49
|
+
mime = 'text/plain' + params;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const buffer = Buffer.from(decodeURIComponent(body), encoding);
|
|
40
53
|
|
|
41
54
|
if (asBlob) {
|
|
42
55
|
if (!_Blob) {
|
|
@@ -7,6 +7,9 @@ export const progressEventReducer = (listener, isDownloadStream, freq = 3) => {
|
|
|
7
7
|
const _speedometer = speedometer(50, 250);
|
|
8
8
|
|
|
9
9
|
return throttle((e) => {
|
|
10
|
+
if (!e || typeof e.loaded !== 'number') {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
10
13
|
const rawLoaded = e.loaded;
|
|
11
14
|
const total = e.lengthComputable ? e.total : undefined;
|
|
12
15
|
const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded;
|
|
@@ -7,11 +7,39 @@ import mergeConfig from '../core/mergeConfig.js';
|
|
|
7
7
|
import AxiosHeaders from '../core/AxiosHeaders.js';
|
|
8
8
|
import buildURL from './buildURL.js';
|
|
9
9
|
|
|
10
|
+
const FORM_DATA_CONTENT_HEADERS = ['content-type', 'content-length'];
|
|
11
|
+
|
|
12
|
+
function setFormDataHeaders(headers, formHeaders, policy) {
|
|
13
|
+
if (policy !== 'content-only') {
|
|
14
|
+
headers.set(formHeaders);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
Object.entries(formHeaders).forEach(([key, val]) => {
|
|
19
|
+
if (FORM_DATA_CONTENT_HEADERS.includes(key.toLowerCase())) {
|
|
20
|
+
headers.set(key, val);
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Encode a UTF-8 string to a Latin-1 byte string for use with btoa().
|
|
27
|
+
* This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern.
|
|
28
|
+
*
|
|
29
|
+
* @param {string} str The string to encode
|
|
30
|
+
*
|
|
31
|
+
* @returns {string} UTF-8 bytes as a Latin-1 string
|
|
32
|
+
*/
|
|
33
|
+
const encodeUTF8 = (str) =>
|
|
34
|
+
encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) =>
|
|
35
|
+
String.fromCharCode(parseInt(hex, 16))
|
|
36
|
+
);
|
|
37
|
+
|
|
10
38
|
export default (config) => {
|
|
11
39
|
const newConfig = mergeConfig({}, config);
|
|
12
40
|
|
|
13
41
|
// Read only own properties to prevent prototype pollution gadgets
|
|
14
|
-
// (e.g. Object.prototype.baseURL = 'https://evil.com').
|
|
42
|
+
// (e.g. Object.prototype.baseURL = 'https://evil.com').
|
|
15
43
|
const own = (key) => (utils.hasOwnProp(newConfig, key) ? newConfig[key] : undefined);
|
|
16
44
|
|
|
17
45
|
const data = own('data');
|
|
@@ -37,11 +65,7 @@ export default (config) => {
|
|
|
37
65
|
headers.set(
|
|
38
66
|
'Authorization',
|
|
39
67
|
'Basic ' +
|
|
40
|
-
btoa(
|
|
41
|
-
(auth.username || '') +
|
|
42
|
-
':' +
|
|
43
|
-
(auth.password ? unescape(encodeURIComponent(auth.password)) : '')
|
|
44
|
-
)
|
|
68
|
+
btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))
|
|
45
69
|
);
|
|
46
70
|
}
|
|
47
71
|
|
|
@@ -50,14 +74,7 @@ export default (config) => {
|
|
|
50
74
|
headers.setContentType(undefined); // browser handles it
|
|
51
75
|
} else if (utils.isFunction(data.getHeaders)) {
|
|
52
76
|
// Node.js FormData (like form-data package)
|
|
53
|
-
|
|
54
|
-
// Only set safe headers to avoid overwriting security headers
|
|
55
|
-
const allowedHeaders = ['content-type', 'content-length'];
|
|
56
|
-
Object.entries(formHeaders).forEach(([key, val]) => {
|
|
57
|
-
if (allowedHeaders.includes(key.toLowerCase())) {
|
|
58
|
-
headers.set(key, val);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
77
|
+
setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy'));
|
|
61
78
|
}
|
|
62
79
|
}
|
|
63
80
|
|
|
@@ -72,10 +89,9 @@ export default (config) => {
|
|
|
72
89
|
|
|
73
90
|
// Strict boolean check — prevents proto-pollution gadgets (e.g. Object.prototype.withXSRFToken = 1)
|
|
74
91
|
// and misconfigurations (e.g. "false") from short-circuiting the same-origin check and leaking
|
|
75
|
-
// the XSRF token cross-origin.
|
|
92
|
+
// the XSRF token cross-origin.
|
|
76
93
|
const shouldSendXSRF =
|
|
77
|
-
withXSRFToken === true ||
|
|
78
|
-
(withXSRFToken == null && isURLSameOrigin(newConfig.url));
|
|
94
|
+
withXSRFToken === true || (withXSRFToken == null && isURLSameOrigin(newConfig.url));
|
|
79
95
|
|
|
80
96
|
if (shouldSendXSRF) {
|
|
81
97
|
const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import utils from '../utils.js';
|
|
4
|
+
|
|
5
|
+
function trimSPorHTAB(str) {
|
|
6
|
+
let start = 0;
|
|
7
|
+
let end = str.length;
|
|
8
|
+
|
|
9
|
+
while (start < end) {
|
|
10
|
+
const code = str.charCodeAt(start);
|
|
11
|
+
|
|
12
|
+
if (code !== 0x09 && code !== 0x20) {
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
start += 1;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
while (end > start) {
|
|
20
|
+
const code = str.charCodeAt(end - 1);
|
|
21
|
+
|
|
22
|
+
if (code !== 0x09 && code !== 0x20) {
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
end -= 1;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return start === 0 && end === str.length ? str : str.slice(start, end);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// The control-code ranges are intentional: header sanitization strips C0/DEL bytes.
|
|
33
|
+
// eslint-disable-next-line no-control-regex
|
|
34
|
+
const INVALID_UNICODE_HEADER_VALUE_CHARS = new RegExp('[\\u0000-\\u0008\\u000a-\\u001f\\u007f]+', 'g');
|
|
35
|
+
// eslint-disable-next-line no-control-regex
|
|
36
|
+
const INVALID_BYTE_STRING_HEADER_VALUE_CHARS = new RegExp('[^\\u0009\\u0020-\\u007e\\u0080-\\u00ff]+', 'g');
|
|
37
|
+
|
|
38
|
+
function sanitizeValue(value, invalidChars) {
|
|
39
|
+
if (utils.isArray(value)) {
|
|
40
|
+
return value.map((item) => sanitizeValue(item, invalidChars));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return trimSPorHTAB(String(value).replace(invalidChars, ''));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const sanitizeHeaderValue = (value) =>
|
|
47
|
+
sanitizeValue(value, INVALID_UNICODE_HEADER_VALUE_CHARS);
|
|
48
|
+
|
|
49
|
+
export const sanitizeByteStringHeaderValue = (value) =>
|
|
50
|
+
sanitizeValue(value, INVALID_BYTE_STRING_HEADER_VALUE_CHARS);
|
|
51
|
+
|
|
52
|
+
export function toByteStringHeaderObject(headers) {
|
|
53
|
+
const byteStringHeaders = Object.create(null);
|
|
54
|
+
|
|
55
|
+
utils.forEach(headers.toJSON(), (value, header) => {
|
|
56
|
+
byteStringHeaders[header] = sanitizeByteStringHeaderValue(value);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return byteStringHeaders;
|
|
60
|
+
}
|