...**/!(*.map|*.min.js)Size
Gzip
Dependencies
Publish
Install
Size
Gzip
Dependencies
Publish
Install
@@ -544,7 +544,7 @@ | |||
| 544 | | LookupAddress | 544 | | LookupAddress |
| 545 | >); | 545 | >); |
| 546 | withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined); | 546 | withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined); |
| 547 | parseReviver?: (this: any, key: string, value: any, context?: { source: string }) => any; | 547 | parseReviver?: (this: any, key: string, value: any, context?: { source?: string }) => any; |
| 548 | fetchOptions?: | 548 | fetchOptions?: |
| 549 | | Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'> | 549 | | Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'> |
| 550 | | Record<string, any>; | 550 | | Record<string, any>; |
@@ -2,46 +2,14 @@ | |||
| 2 | 2 | ||
| 3 | import utils from '../utils.js'; | 3 | import utils from '../utils.js'; |
| 4 | import parseHeaders from '../helpers/parseHeaders.js'; | 4 | import parseHeaders from '../helpers/parseHeaders.js'; |
| 5 | import { sanitizeHeaderValue } from '../helpers/sanitizeHeaderValue.js'; | ||
| 5 | 6 | ||
| 6 | const $internals = Symbol('internals'); | 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 | function normalizeHeader(header) { | 9 | function normalizeHeader(header) { |
| 38 | return header && String(header).trim().toLowerCase(); | 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 | function normalizeValue(value) { | 13 | function normalizeValue(value) { |
| 46 | if (value === false || value == null) { | 14 | if (value === false || value == null) { |
| 47 | return value; | 15 | return value; |
@@ -3,54 +3,55 @@ | |||
| 3 | import utils from '../utils.js'; | 3 | import utils from '../utils.js'; |
| 4 | 4 | ||
| 5 | const composeSignals = (signals, timeout) => { | 5 | const composeSignals = (signals, timeout) => { |
| 6 | const { length } = (signals = signals ? signals.filter(Boolean) : []); | 6 | signals = signals ? signals.filter(Boolean) : []; |
| 7 | 7 | ||
| 8 | if (timeout || length) { | ||
| 9 | let controller = new AbortController(); | 8 | if (!timeout && !signals.length) { |
| 9 | return; | ||
| 10 | } | ||
| 10 | 11 | ||
| 11 | let aborted; | 12 | const controller = new AbortController(); |
| 12 | 13 | ||
| 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 | }; | 14 | let aborted = false; |
| 25 | 15 | ||
| 26 | let timer = | ||
| 27 | timeout && | ||
| 28 | setTimeout(() => { | ||
| 29 | timer = null; | ||
| 30 | onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT)); | ||
| 31 | }, timeout); | 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 | }; | ||
| 32 | 28 | ||
| 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 | }; | 29 | let timer = |
| 30 | timeout && | ||
| 31 | setTimeout(() => { | ||
| 32 | timer = null; | ||
| 33 | onabort(new AxiosError(`timeout of ${timeout}ms exceeded`, AxiosError.ETIMEDOUT)); | ||
| 34 | }, timeout); | ||
| 45 | 35 | ||
| 46 | signals.forEach((signal) => signal.addEventListener('abort', onabort)); | 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 | 47 | ||
| 48 | const { signal } = controller; | 48 | signals.forEach((signal) => signal.addEventListener('abort', onabort)); |
| 49 | 49 | ||
| 50 | signal.unsubscribe = () => utils.asap(unsubscribe); | 50 | const { signal } = controller; |
| 51 | 51 | ||
| 52 | return signal; | ||
| 53 | } | 52 | signal.unsubscribe = () => utils.asap(unsubscribe); |
| 53 | |||
| 54 | return signal; | ||
| 54 | }; | 55 | }; |
| 55 | 56 | ||
| 56 | export default composeSignals; | 57 | export default composeSignals; |
@@ -13,6 +13,7 @@ | |||
| 13 | import settle from '../core/settle.js'; | 13 | import settle from '../core/settle.js'; |
| 14 | import estimateDataURLDecodedBytes from '../helpers/estimateDataURLDecodedBytes.js'; | 14 | import estimateDataURLDecodedBytes from '../helpers/estimateDataURLDecodedBytes.js'; |
| 15 | import { VERSION } from '../env/data.js'; | 15 | import { VERSION } from '../env/data.js'; |
| 16 | import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js'; | ||
| 16 | 17 | ||
| 17 | const DEFAULT_CHUNK_SIZE = 64 * 1024; | 18 | const DEFAULT_CHUNK_SIZE = 64 * 1024; |
| 18 | 19 | ||
@@ -27,7 +28,10 @@ | |||
| 27 | }; | 28 | }; |
| 28 | 29 | ||
| 29 | const factory = (env) => { | 30 | const factory = (env) => { |
| 30 | const globalObject = utils.global ?? globalThis; | 31 | const globalObject = |
| 32 | utils.global !== undefined && utils.global !== null | ||
| 33 | ? utils.global | ||
| 34 | : globalThis; | ||
| 31 | const { ReadableStream, TextEncoder } = globalObject; | 35 | const { ReadableStream, TextEncoder } = globalObject; |
| 32 | 36 | ||
| 33 | env = utils.merge.call( | 37 | env = utils.merge.call( |
@@ -284,7 +288,7 @@ | |||
| 284 | ...fetchOptions, | 288 | ...fetchOptions, |
| 285 | signal: composedSignal, | 289 | signal: composedSignal, |
| 286 | method: method.toUpperCase(), | 290 | method: method.toUpperCase(), |
| 287 | headers: headers.normalize().toJSON(), | 291 | headers: toByteStringHeaderObject(headers.normalize()), |
| 288 | body: data, | 292 | body: data, |
| 289 | duplex: 'half', | 293 | duplex: 'half', |
| 290 | credentials: isCredentialsSupported ? withCredentials : undefined, | 294 | credentials: isCredentialsSupported ? withCredentials : undefined, |
@@ -4,7 +4,9 @@ | |||
| 4 | import parseProtocol from './parseProtocol.js'; | 4 | import parseProtocol from './parseProtocol.js'; |
| 5 | import platform from '../platform/index.js'; | 5 | import platform from '../platform/index.js'; |
| 6 | 6 | ||
| 7 | const DATA_URL_PATTERN = /^(?:([^;]+);)?(?:[^;]+;)?(base64|),([\s\S]*)$/; | 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 | * Parse data uri to a Buffer or Blob | 12 | * Parse data uri to a Buffer or Blob |
@@ -33,11 +35,22 @@ | |||
| 33 | throw new AxiosError('Invalid URL', AxiosError.ERR_INVALID_URL); | 35 | throw new AxiosError('Invalid URL', AxiosError.ERR_INVALID_URL); |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | const mime = match[1]; | ||
| 37 | const isBase64 = match[2]; | ||
| 38 | const body = match[3]; | ||
| 39 | const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8'); | 38 | const type = match[1]; |
| 39 | const params = match[2]; | ||
| 40 | const encoding = match[3] ? 'base64' : 'utf8'; | ||
| 41 | const body = match[4]; | ||
| 40 | 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); | ||
| 53 | |||
| 41 | if (asBlob) { | 54 | if (asBlob) { |
| 42 | if (!_Blob) { | 55 | if (!_Blob) { |
| 43 | throw new AxiosError('Blob is not supported', AxiosError.ERR_NOT_SUPPORT); | 56 | throw new AxiosError('Blob is not supported', AxiosError.ERR_NOT_SUPPORT); |
@@ -7,6 +7,9 @@ | |||
| 7 | const _speedometer = speedometer(50, 250); | 7 | const _speedometer = speedometer(50, 250); |
| 8 | 8 | ||
| 9 | return throttle((e) => { | 9 | return throttle((e) => { |
| 10 | if (!e || typeof e.loaded !== 'number') { | ||
| 11 | return; | ||
| 12 | } | ||
| 10 | const rawLoaded = e.loaded; | 13 | const rawLoaded = e.loaded; |
| 11 | const total = e.lengthComputable ? e.total : undefined; | 14 | const total = e.lengthComputable ? e.total : undefined; |
| 12 | const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded; | 15 | const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded; |
@@ -763,11 +763,11 @@ | |||
| 763 | * @returns {Object} The JSON-compatible object. | 763 | * @returns {Object} The JSON-compatible object. |
| 764 | */ | 764 | */ |
| 765 | const toJSONObject = (obj) => { | 765 | const toJSONObject = (obj) => { |
| 766 | const stack = new Array(10); | 766 | const visited = new WeakSet(); |
| 767 | 767 | ||
| 768 | const visit = (source, i) => { | 768 | const visit = (source) => { |
| 769 | if (isObject(source)) { | 769 | if (isObject(source)) { |
| 770 | if (stack.indexOf(source) >= 0) { | 770 | if (visited.has(source)) { |
| 771 | return; | 771 | return; |
| 772 | } | 772 | } |
| 773 | 773 | ||
@@ -777,15 +777,16 @@ | |||
| 777 | } | 777 | } |
| 778 | 778 | ||
| 779 | if (!('toJSON' in source)) { | 779 | if (!('toJSON' in source)) { |
| 780 | stack[i] = source; | 780 | // add-on descent / delete-on-ascent: preserves path semantics, so DAG nodes serialise at every occurrence (see #7230). |
| 781 | visited.add(source); | ||
| 781 | const target = isArray(source) ? [] : {}; | 782 | const target = isArray(source) ? [] : {}; |
| 782 | 783 | ||
| 783 | forEach(source, (value, key) => { | 784 | forEach(source, (value, key) => { |
| 784 | const reducedValue = visit(value, i + 1); | 785 | const reducedValue = visit(value); |
| 785 | !isUndefined(reducedValue) && (target[key] = reducedValue); | 786 | !isUndefined(reducedValue) && (target[key] = reducedValue); |
| 786 | }); | 787 | }); |
| 787 | 788 | ||
| 788 | stack[i] = undefined; | 789 | visited.delete(source); |
| 789 | 790 | ||
| 790 | return target; | 791 | return target; |
| 791 | } | 792 | } |
@@ -794,7 +795,7 @@ | |||
| 794 | return source; | 795 | return source; |
| 795 | }; | 796 | }; |
| 796 | 797 | ||
| 797 | return visit(obj, 0); | 798 | return visit(obj); |
| 798 | }; | 799 | }; |
| 799 | 800 | ||
| 800 | /** | 801 | /** |
@@ -8,6 +8,7 @@ | |||
| 8 | import AxiosHeaders from '../core/AxiosHeaders.js'; | 8 | import AxiosHeaders from '../core/AxiosHeaders.js'; |
| 9 | import { progressEventReducer } from '../helpers/progressEventReducer.js'; | 9 | import { progressEventReducer } from '../helpers/progressEventReducer.js'; |
| 10 | import resolveConfig from '../helpers/resolveConfig.js'; | 10 | import resolveConfig from '../helpers/resolveConfig.js'; |
| 11 | import { toByteStringHeaderObject } from '../helpers/sanitizeHeaderValue.js'; | ||
| 11 | 12 | ||
| 12 | const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined'; | 13 | const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined'; |
| 13 | 14 | ||
@@ -156,7 +157,7 @@ | |||
| 156 | 157 | ||
| 157 | // Add headers to the request | 158 | // Add headers to the request |
| 158 | if ('setRequestHeader' in request) { | 159 | if ('setRequestHeader' in request) { |
| 159 | utils.forEach(requestHeaders.toJSON(), function setRequestHeader(val, key) { | 160 | utils.forEach(toByteStringHeaderObject(requestHeaders), function setRequestHeader(val, key) { |
| 160 | request.setRequestHeader(key, val); | 161 | request.setRequestHeader(key, val); |
| 161 | }); | 162 | }); |
| 162 | } | 163 | } |
@@ -1,6 +1,6 @@ | |||
| 1 | { | 1 | { |
| 2 | "name": "axios", | 2 | "name": "axios", |
| 3 | "version": "1.16.0", | 3 | "version": "1.16.1", |
| 4 | "description": "Promise based HTTP client for the browser and node.js", | 4 | "description": "Promise based HTTP client for the browser and node.js", |
| 5 | "main": "./dist/node/axios.cjs", | 5 | "main": "./dist/node/axios.cjs", |
| 6 | "module": "./index.js", | 6 | "module": "./index.js", |
@@ -123,6 +123,7 @@ | |||
| 123 | "dependencies": { | 123 | "dependencies": { |
| 124 | "follow-redirects": "^1.16.0", | 124 | "follow-redirects": "^1.16.0", |
| 125 | "form-data": "^4.0.5", | 125 | "form-data": "^4.0.5", |
| 126 | "https-proxy-agent": "^5.0.1", | ||
| 126 | "proxy-from-env": "^2.1.0" | 127 | "proxy-from-env": "^2.1.0" |
| 127 | }, | 128 | }, |
| 128 | "devDependencies": { | 129 | "devDependencies": { |
@@ -140,6 +141,7 @@ | |||
| 140 | "@vitest/browser": "^4.1.5", | 141 | "@vitest/browser": "^4.1.5", |
| 141 | "@vitest/browser-playwright": "^4.1.5", | 142 | "@vitest/browser-playwright": "^4.1.5", |
| 142 | "abortcontroller-polyfill": "^1.7.8", | 143 | "abortcontroller-polyfill": "^1.7.8", |
| 144 | "acorn": "^8.16.0", | ||
| 143 | "body-parser": "^2.2.2", | 145 | "body-parser": "^2.2.2", |
| 144 | "chalk": "^5.6.2", | 146 | "chalk": "^5.6.2", |
| 145 | "cross-env": "^10.1.0", | 147 | "cross-env": "^10.1.0", |
@@ -1,5 +1,76 @@ | |||
| 1 | # Changelog | 1 | # Changelog |
| 2 | 2 | ||
| 3 | ## v1.16.0 — May 2, 2026 | ||
| 4 | |||
| 5 | This release adds support for the QUERY HTTP method and a new `ECONNREFUSED` error constant, lands a substantial wave of HTTP, fetch, and XHR adapter bug fixes around redirects, aborts, headers, and timeouts, and welcomes 23 new contributors. | ||
| 6 | |||
| 7 | ## ⚠️ Notable Changes | ||
| 8 | |||
| 9 | A handful of fixes in this release are either security-adjacent or change observable behaviour. Please review before upgrading: | ||
| 10 | |||
| 11 | - **Fetch adapter now enforces `maxBodyLength` and `maxContentLength`.** These limits were silently ignored on the fetch adapter prior to 1.16.0 — anyone relying on them as a safety net (DoS protection, accidental large uploads) had no protection. (**#10795**) | ||
| 12 | - **Proxy requests now preserve user-supplied `Host` headers.** Previously, the proxy path could overwrite a custom `Host`. Virtual-host-style routing through a proxy will now behave correctly. (**#10822**) | ||
| 13 | - **Basic auth credentials embedded in URLs are now URL-decoded.** If you have percent-encoded credentials in a URL (e.g. `https://user:p%40ss@host`), the decoded value is what now goes on the wire. (**#10825**) | ||
| 14 | - **`parseProtocol` now strictly requires a colon in the protocol separator.** Strings that loosely parsed as protocols before may no longer match. (**#10729**) | ||
| 15 | - **Deprecated `unescape()` replaced with modern UTF-8 encoding.** Non-ASCII URL handling is now spec-correct; consumers depending on legacy `unescape()` quirks may see different output bytes. (**#7378**) | ||
| 16 | - **`transformRequest` input typing change was reverted.** The typing change introduced in #10745 was reverted in #10810 after follow-up review — net behavior is unchanged from 1.15.2. (**#10745**, **#10810**) | ||
| 17 | |||
| 18 | ## 🚀 New Features | ||
| 19 | |||
| 20 | - **QUERY HTTP Method:** Added support for the QUERY HTTP method across adapters and type definitions. (**#10802**) | ||
| 21 | - **ECONNREFUSED Error Constant:** Exposed `ECONNREFUSED` as a constant on `AxiosError` so callers can match connection-refused failures without comparing string literals (closes #6485). (**#10680**) | ||
| 22 | - **Encode Helper Export:** Exported the internal `encode` helper from `buildURL` so userland param serializers can reuse the same encoding logic that axios uses internally. (**#6897**) | ||
| 23 | |||
| 24 | ## 🐛 Bug Fixes | ||
| 25 | |||
| 26 | - **HTTP Adapter — Redirects & Headers:** Cleared stale headers when a redirect targets a no-proxy host, fixed the redirect listener chain so listeners no longer stack across hops, restored the missing `requestDetails` argument on `beforeRedirect`, preserved user-supplied `Host` headers when forwarding through a proxy, and properly URL-decoded basic auth credentials. (**#10794**, **#10800**, **#6241**, **#10822**, **#10825**) | ||
| 27 | - **HTTP Adapter — Streams & Timeouts:** Preserved the partial response object on `AxiosError` when a stream is aborted after headers arrive, honoured the `timeout` option during the connect phase when redirects are disabled, and resolved an unsettled-promise hang when an aborted request was combined with compression and `maxRedirects: 0`. (**#10708**, **#10819**, **#7149**) | ||
| 28 | - **Fetch Adapter:** Enforced `maxBodyLength` / `maxContentLength` in the fetch adapter, set the `User-Agent` header to match the HTTP adapter, preserved the original abort reason instead of replacing it with a generic error, and deferred global access so importing the module no longer throws a `TypeError` in restricted environments. (**#10795**, **#10772**, **#10806**, **#7260**) | ||
| 29 | - **XHR Adapter:** Unsubscribed the `cancelToken` and `AbortSignal` listeners on the error, timeout, and abort code paths to prevent leaked subscriptions. (**#10787**) | ||
| 30 | - **Error Handling:** Attached the parsed response to `AxiosError` when `JSON.parse` fails inside `dispatchRequest`, prevented `settle` from emitting `undefined` error codes, and tightened the `parseProtocol` regex to require a colon in the protocol separator. (**#10724**, **#7276**, **#10729**) | ||
| 31 | - **Types & Exports:** Aligned the CommonJS `CancelToken` typings with the ESM build, fixed a compiler error caused by `RawAxiosHeaders`, and re-exported `create` from the package index. (**#7414**, **#6389**, **#6460**) | ||
| 32 | - **UTF-8 Encoding:** Replaced the deprecated `unescape()` call with a modern UTF-8 encoding implementation. (**#7378**) | ||
| 33 | - **Misc Cleanup:** Resolved a batch of small inconsistencies and gadget-level issues across the codebase. (**#10833**) | ||
| 34 | |||
| 35 | ## 🔧 Maintenance & Chores | ||
| 36 | |||
| 37 | - **Refactor — ES6 Modernisation:** Modernised the `utils` module and XHR adapter to use ES6 features, and tidied the multipart boundary error message. (**#10588**, **#7419**) | ||
| 38 | - **Tests:** Hardened the HTTP test server lifecycle to fix flaky `FormData` EPIPE failures, fixed Win32 platform support for the pipe tests, and corrected an incorrect test assumption. (**#10820**, **#10791**, **#10796**) | ||
| 39 | - **Docs:** Documented `paramsSerializer.encode` for strict RFC 3986 query encoding, updated the `parseReviver` TypeScript definitions and configuration docs for ES2023, added timeout guidance to the README's first async example, and expanded notes around the recent type changes. (**#10821**, **#10782**, **#10759**, **#10804**) | ||
| 40 | - **Reverted:** Reverted the `transformRequest` input typing change from #10745 after follow-up review. (**#10745**, **#10810**) | ||
| 41 | - **Dependencies:** Bumped `actions/setup-node`, the `github-actions` group, and `postcss` (in `/docs`) to their latest versions. (**#10785**, **#10813**, **#10814**) | ||
| 42 | - **Release:** Updated changelog and packages, and prepared the 1.16.0 release. (**#10790**, **#10834**) | ||
| 43 | |||
| 44 | ## 🌟 New Contributors | ||
| 45 | |||
| 46 | We are thrilled to welcome our new contributors. Thank you for helping improve axios: | ||
| 47 | |||
| 48 | - **@singhankit001** (**#10588**) | ||
| 49 | - **@cuiweixie** (**#7419**) | ||
| 50 | - **@iruizsalinas** (**#10787**) | ||
| 51 | - **@MarcosNocetti** (**#10680**) | ||
| 52 | - **@deepview-autofix** (**#10729**) | ||
| 53 | - **@atharvasingh7007** (**#10745**) | ||
| 54 | - **@OfekDanny** (**#10772**) | ||
| 55 | - **@mnahkies** (**#7414**) | ||
| 56 | - **@tboyila** (**#10759**) | ||
| 57 | - **@Kingo64** (**#6897**) | ||
| 58 | - **@ramram1048** (**#6389**) | ||
| 59 | - **@FLNacif** (**#6460**) | ||
| 60 | - **@zozo123** (**#10806**) | ||
| 61 | - **@pierluigilenoci** (**#10802**) | ||
| 62 | - **@afurm** (**#10708**) | ||
| 63 | - **@karan-lrn** (**#7378**) | ||
| 64 | - **@ebeigarts** (**#7149**) | ||
| 65 | - **@Raymondo97** (**#10782**) | ||
| 66 | - **@mixelburg** (**#10821**) | ||
| 67 | - **@ashishkr96** (**#10822**) | ||
| 68 | - **@cyphercodes** (**#10819**) | ||
| 69 | - **@Jye10032** (**#7260**) | ||
| 70 | - **@VeerShah41** (**#7276**) | ||
| 71 | |||
| 72 | [Full Changelog](https://github.com/axios/axios/compare/v1.15.2...v1.16.0) | ||
| 73 | |||
| 3 | ## v1.15.2 - April 21, 2026 | 74 | ## v1.15.2 - April 21, 2026 |
| 4 | 75 | ||
| 5 | This release delivers prototype-pollution hardening for the Node HTTP adapter, adds an opt-in `allowedSocketPaths` allowlist to mitigate SSRF via Unix domain sockets, fixes a keep-alive socket memory leak, and ships supply-chain hardening across CI and security docs. | 76 | This release delivers prototype-pollution hardening for the Node HTTP adapter, adds an opt-in `allowedSocketPaths` allowlist to mitigate SSRF via Unix domain sockets, fixes a keep-alive socket memory leak, and ships supply-chain hardening across CI and security docs. |
@@ -244,14 +244,13 @@ | |||
| 244 | width="71px" | 244 | width="71px" |
| 245 | height="70px" | 245 | height="70px" |
| 246 | src="https://images.opencollective.com/buzzoid-buy-instagram-followers/56a09fe/logo.png" | 246 | src="https://images.opencollective.com/buzzoid-buy-instagram-followers/56a09fe/logo.png" |
| 247 | alt="Buzzoid" | 247 | alt="Buzzoid - Buy Instagram Followers" |
| 248 | /> | 248 | /> |
| 249 | </a> | 249 | </a> |
| 250 | <p | 250 | <p |
| 251 | align="center" | 251 | align="center" |
| 252 | > | 252 | > |
| 253 | A lightweight open-source API Development, Testing & Mocking | ||
| 254 | platform | 253 | At Buzzoid, you can buy Instagram followers quickly, safely, and easily with just a few clicks. Rated world's #1 IG service since 2012. |
| 255 | </p> | 254 | </p> |
| 256 | <p align="center"> | 255 | <p align="center"> |
| 257 | <a | 256 | <a |
@@ -263,10 +262,29 @@ | |||
| 263 | </td> | 262 | </td> |
| 264 | <td align="center" width="33.333333333333336%"> | 263 | <td align="center" width="33.333333333333336%"> |
| 265 | <a | 264 | <a |
| 266 | href="https://opencollective.com/axios/contribute" | 265 | href="https://twicsy.com/buy-instagram-followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship" |
| 266 | style="padding: 10px; display: inline-block" | ||
| 267 | target="_blank" | 267 | target="_blank" |
| 268 | >💜 Become a sponsor</a | ||
| 269 | > | 268 | > |
| 269 | <img | ||
| 270 | width="71px" | ||
| 271 | height="70px" | ||
| 272 | src="https://images.opencollective.com/buy-instagram-followers-twicsy/b4c5d7f/logo/256.png?height=256" | ||
| 273 | alt="Buy Instagram Followers Twicsy" | ||
| 274 | /> | ||
| 275 | </a> | ||
| 276 | <p | ||
| 277 | align="center" | ||
| 278 | > | ||
| 279 | Buy real Instagram followers from Twicsy. Twicsy has been voted the best site to buy followers from the likes of US Magazine. | ||
| 280 | </p> | ||
| 281 | <p align="center"> | ||
| 282 | <a | ||
| 283 | href="https://twicsy.com/buy-instagram-followers/?utm_source=axios_docs_website&utm_medium=website&utm_campaign=axios_open_collective_sponsorship" | ||
| 284 | target="_blank" | ||
| 285 | ><b>twicsy.com</b></a | ||
| 286 | > | ||
| 287 | </p> | ||
| 270 | </td> | 288 | </td> |
| 271 | <td align="center" width="33.333333333333336%"> | 289 | <td align="center" width="33.333333333333336%"> |
| 272 | <a | 290 | <a |
@@ -495,8 +513,16 @@ | |||
| 495 | // Want to use async/await? Add the `async` keyword to your outer function/method. | 513 | // Want to use async/await? Add the `async` keyword to your outer function/method. |
| 496 | async function getUser() { | 514 | async function getUser() { |
| 497 | try { | 515 | try { |
| 498 | const response = await axios.get('/user?ID=12345'); | ||
| 499 | console.log(response); | 516 | // Example: GET request with query parameters |
| 517 | const response = await axios.get('/user', { | ||
| 518 | params: { | ||
| 519 | ID: 12345 | ||
| 520 | } | ||
| 521 | }); | ||
| 522 | |||
| 523 | // Using the `params` option improves readability and automatically formats query strings | ||
| 524 | |||
| 525 | console.log(response); | ||
| 500 | } catch (error) { | 526 | } catch (error) { |
| 501 | console.error(error); | 527 | console.error(error); |
| 502 | } | 528 | } |
@@ -934,15 +960,24 @@ | |||
| 934 | // Use `false` to disable proxies, ignoring environment variables. | 960 | // Use `false` to disable proxies, ignoring environment variables. |
| 935 | // `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and | 961 | // `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and |
| 936 | // supplies credentials. | 962 | // supplies credentials. |
| 937 | // This will set a `Proxy-Authorization` header, overwriting any existing | ||
| 938 | // `Proxy-Authorization` custom headers you have set using `headers`. | 963 | // For `http://` targets, axios sends the request to the proxy in |
| 964 | // forward-proxy mode and stamps `Proxy-Authorization` onto the request | ||
| 965 | // headers (overwriting any user-supplied `Proxy-Authorization` header). | ||
| 966 | // For `https://` targets, axios establishes a CONNECT tunnel through the | ||
| 967 | // proxy and performs TLS end-to-end with the origin; `Proxy-Authorization` | ||
| 968 | // is sent on the CONNECT request only, never on the wrapped TLS request, | ||
| 969 | // so the proxy never sees the URL, headers, or body. Supply a custom | ||
| 970 | // `httpsAgent` to opt out of automatic CONNECT tunneling. | ||
| 939 | // If the proxy server uses HTTPS, then you must set the protocol to `https`. | 971 | // If the proxy server uses HTTPS, then you must set the protocol to `https`. |
| 940 | // A user-supplied `Host` header in `headers` is preserved when forwarding | 972 | // A user-supplied `Host` header in `headers` is preserved when forwarding |
| 941 | // through a proxy (case-insensitive match on `host`/`Host`/`HOST`); this | 973 | // through a proxy (case-insensitive match on `host`/`Host`/`HOST`); this |
| 942 | // lets you target a virtual host that differs from the request URL — for | 974 | // lets you target a virtual host that differs from the request URL — for |
| 943 | // example, hitting `127.0.0.1:4000` while having the proxy treat the | 975 | // example, hitting `127.0.0.1:4000` while having the proxy treat the |
| 944 | // request as `example.com`. If no `Host` header is supplied, axios | 976 | // request as `example.com`. If no `Host` header is supplied, axios |
| 945 | // defaults it to the request URL's `hostname:port` as before. | 977 | // defaults it to the request URL's `hostname:port` as before. The Host |
| 978 | // header is only set in forward-proxy mode (HTTP targets); for HTTPS | ||
| 979 | // tunneling the Host header is sent inside the TLS connection, not seen | ||
| 980 | // by the proxy. | ||
| 946 | proxy: { | 981 | proxy: { |
| 947 | protocol: 'https', | 982 | protocol: 'https', |
| 948 | host: '127.0.0.1', | 983 | host: '127.0.0.1', |
@@ -1180,7 +1215,7 @@ | |||
| 1180 | const myInterceptor = instance.interceptors.request.use(function () { | 1215 | const myInterceptor = instance.interceptors.request.use(function () { |
| 1181 | /*...*/ | 1216 | /*...*/ |
| 1182 | }); | 1217 | }); |
| 1183 | axios.interceptors.request.eject(myInterceptor); | 1218 | instance.interceptors.request.eject(myInterceptor); |
| 1184 | ``` | 1219 | ``` |
| 1185 | 1220 | ||
| 1186 | You can also clear all interceptors for requests or responses. | 1221 | You can also clear all interceptors for requests or responses. |
@@ -441,7 +441,7 @@ | |||
| 441 | [address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress | 441 | [address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress |
| 442 | >); | 442 | >); |
| 443 | withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined); | 443 | withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined); |
| 444 | parseReviver?: (this: any, key: string, value: any, context?: { source: string }) => any; | 444 | parseReviver?: (this: any, key: string, value: any, context?: { source?: string }) => any; |
| 445 | fetchOptions?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'> | Record<string, any>; | 445 | fetchOptions?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'> | Record<string, any>; |
| 446 | httpVersion?: 1 | 2; | 446 | httpVersion?: 1 | 2; |
| 447 | http2Options?: Record<string, any> & { | 447 | http2Options?: Record<string, any> & { |
@@ -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 | } |