...**/!(*.map|*.min.js)Size
Gzip
Dependencies
Publish
Install
Size
Gzip
Dependencies
Publish
Install
@@ -66,7 +66,9 @@ | ||
| 66 | 66 | ...targets: Array<AxiosHeaders | axios.RawAxiosHeaders | string | undefined | null> |
| 67 | 67 | ): AxiosHeaders; |
| 68 | 68 | |
| 69 | toJSON(asStrings?: boolean): axios.RawAxiosHeaders; | |
| 69 | toJSON(asStrings: true): Record<string, string>; | |
| 70 | toJSON(asStrings?: false): Record<string, string | string[]>; | |
| 71 | toJSON(asStrings?: boolean): Record<string, string | string[]>; | |
| 70 | 72 | |
| 71 | 73 | static from(thing?: AxiosHeaders | axios.RawAxiosHeaders | string): AxiosHeaders; |
| 72 | 74 | |
@@ -162,7 +164,9 @@ | ||
| 162 | 164 | static readonly ETIMEDOUT = 'ETIMEDOUT'; |
| 163 | 165 | } |
| 164 | 166 | |
| 165 | declare class CanceledError<T> extends AxiosError<T> {} | |
| 167 | declare class CanceledError<T> extends AxiosError<T> { | |
| 168 | readonly name: 'CanceledError'; | |
| 169 | } | |
| 166 | 170 | |
| 167 | 171 | declare class Axios { |
| 168 | 172 | constructor(config?: axios.AxiosRequestConfig); |
@@ -392,6 +396,7 @@ | ||
| 392 | 396 | forcedJSONParsing?: boolean; |
| 393 | 397 | clarifyTimeoutError?: boolean; |
| 394 | 398 | legacyInterceptorReqResOrdering?: boolean; |
| 399 | advertiseZstdAcceptEncoding?: boolean; | |
| 395 | 400 | } |
| 396 | 401 | |
| 397 | 402 | interface GenericAbortSignal { |
@@ -691,7 +696,7 @@ | ||
| 691 | 696 | CanceledError: typeof CanceledError; |
| 692 | 697 | HttpStatusCode: typeof HttpStatusCode; |
| 693 | 698 | readonly VERSION: string; |
| 694 | isCancel(value: any): value is Cancel; | |
| 699 | isCancel<T = any>(value: any): value is CanceledError<T>; | |
| 695 | 700 | all<T>(values: Array<T | Promise<T>>): Promise<T[]>; |
| 696 | 701 | spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R; |
| 697 | 702 | isAxiosError<T = any, D = any>(payload: any): payload is AxiosError<T, D>; |
@@ -101,6 +101,7 @@ | ||
| 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 | ); |
@@ -89,7 +89,7 @@ | ||
| 89 | 89 | const lHeader = normalizeHeader(_header); |
| 90 | 90 | |
| 91 | 91 | if (!lHeader) { |
| 92 | throw new Error('header name must be a non-empty string'); | |
| 92 | return; | |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | 95 | const key = utils.findKey(self, lHeader); |
@@ -117,7 +117,7 @@ | ||
| 117 | 117 | key; |
| 118 | 118 | for (const entry of header) { |
| 119 | 119 | if (!utils.isArray(entry)) { |
| 120 | throw TypeError('Object iterator must return a key-value pair'); | |
| 120 | throw new TypeError('Object iterator must return a key-value pair'); | |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | 123 | obj[(key = entry[0])] = (dest = obj[key]) |
@@ -19,6 +19,35 @@ | ||
| 19 | 19 | |
| 20 | 20 | const { isFunction } = utils; |
| 21 | 21 | |
| 22 | /** | |
| 23 | * Encode a UTF-8 string to a Latin-1 byte string for use with btoa(). | |
| 24 | * This is a modern replacement for the deprecated unescape(encodeURIComponent(str)) pattern. | |
| 25 | * | |
| 26 | * @param {string} str The string to encode | |
| 27 | * | |
| 28 | * @returns {string} UTF-8 bytes as a Latin-1 string | |
| 29 | */ | |
| 30 | const encodeUTF8 = (str) => | |
| 31 | encodeURIComponent(str).replace(/%([0-9A-F]{2})/gi, (_, hex) => | |
| 32 | String.fromCharCode(parseInt(hex, 16)) | |
| 33 | ); | |
| 34 | ||
| 35 | // Node's WHATWG URL parser returns `username` and `password` percent-encoded. | |
| 36 | // Decode before composing the `auth` option so credentials such as | |
| 37 | // `my%40email.com:pass` are sent as `my@email.com:pass`. Falls back to the | |
| 38 | // original value for malformed input so a bad encoding never throws. | |
| 39 | const decodeURIComponentSafe = (value) => { | |
| 40 | if (!utils.isString(value)) { | |
| 41 | return value; | |
| 42 | } | |
| 43 | ||
| 44 | try { | |
| 45 | return decodeURIComponent(value); | |
| 46 | } catch (error) { | |
| 47 | return value; | |
| 48 | } | |
| 49 | }; | |
| 50 | ||
| 22 | 51 | const test = (fn, ...args) => { |
| 23 | 52 | try { |
| 24 | 53 | return !!fn(...args); |
@@ -27,6 +56,15 @@ | ||
| 27 | 56 | } |
| 28 | 57 | }; |
| 29 | 58 | |
| 59 | const maybeWithAuthCredentials = (url) => { | |
| 60 | const protocolIndex = url.indexOf('://'); | |
| 61 | let urlToCheck = url; | |
| 62 | if (protocolIndex !== -1) { | |
| 63 | urlToCheck = urlToCheck.slice(protocolIndex + 3); | |
| 64 | } | |
| 65 | return urlToCheck.includes('@') || urlToCheck.includes(':'); | |
| 66 | }; | |
| 67 | ||
| 30 | 68 | const factory = (env) => { |
| 31 | 69 | const globalObject = |
| 32 | 70 | utils.global !== undefined && utils.global !== null |
@@ -174,6 +212,7 @@ | ||
| 174 | 212 | |
| 175 | 213 | const hasMaxContentLength = utils.isNumber(maxContentLength) && maxContentLength > -1; |
| 176 | 214 | const hasMaxBodyLength = utils.isNumber(maxBodyLength) && maxBodyLength > -1; |
| 215 | const own = (key) => (utils.hasOwnProp(config, key) ? config[key] : undefined); | |
| 177 | 216 | |
| 178 | 217 | let _fetch = envFetch || fetch; |
| 179 | 218 | |
@@ -196,6 +235,46 @@ | ||
| 196 | 235 | let requestContentLength; |
| 197 | 236 | |
| 198 | 237 | try { |
| 238 | // HTTP basic authentication | |
| 239 | let auth = undefined; | |
| 240 | const configAuth = own('auth'); | |
| 241 | ||
| 242 | if (configAuth) { | |
| 243 | const username = configAuth.username || ''; | |
| 244 | const password = configAuth.password || ''; | |
| 245 | auth = { | |
| 246 | username, | |
| 247 | password | |
| 248 | }; | |
| 249 | } | |
| 250 | ||
| 251 | if (maybeWithAuthCredentials(url)) { | |
| 252 | const parsedURL = new URL(url, platform.origin); | |
| 253 | ||
| 254 | if (!auth && (parsedURL.username || parsedURL.password)) { | |
| 255 | const urlUsername = decodeURIComponentSafe(parsedURL.username); | |
| 256 | const urlPassword = decodeURIComponentSafe(parsedURL.password); | |
| 257 | auth = { | |
| 258 | username: urlUsername, | |
| 259 | password: urlPassword | |
| 260 | }; | |
| 261 | } | |
| 262 | ||
| 263 | if (parsedURL.username || parsedURL.password) { | |
| 264 | parsedURL.username = ''; | |
| 265 | parsedURL.password = ''; | |
| 266 | url = parsedURL.href; | |
| 267 | } | |
| 268 | } | |
| 269 | ||
| 270 | if (auth) { | |
| 271 | headers.delete('authorization'); | |
| 272 | headers.set( | |
| 273 | 'Authorization', | |
| 274 | 'Basic ' + btoa(encodeUTF8((auth.username || '') + ':' + (auth.password || ''))) | |
| 275 | ); | |
| 276 | } | |
| 277 | ||
| 199 | 278 | // Enforce maxContentLength for data: URLs up-front so we never materialize |
| 200 | 279 | // an oversized payload. The HTTP adapter applies the same check (see http.js |
| 201 | 280 | // "if (protocol === 'data:')" branch). |
@@ -73,11 +73,11 @@ | ||
| 73 | 73 | } = options || {}; |
| 74 | 74 | |
| 75 | 75 | if (!utils.isFormData(form)) { |
| 76 | throw TypeError('FormData instance required'); | |
| 76 | throw new TypeError('FormData instance required'); | |
| 77 | 77 | } |
| 78 | 78 | |
| 79 | 79 | if (boundary.length < 1 || boundary.length > 70) { |
| 80 | throw Error('boundary must be 1-70 characters long'); | |
| 80 | throw new Error('boundary must be 1-70 characters long'); | |
| 81 | 81 | } |
| 82 | 82 | |
| 83 | 83 | const boundaryBytes = textEncoder.encode('--' + boundary + CRLF); |
@@ -35,7 +35,7 @@ | ||
| 35 | 35 | String.fromCharCode(parseInt(hex, 16)) |
| 36 | 36 | ); |
| 37 | 37 | |
| 38 | export default (config) => { | |
| 38 | function resolveConfig(config) { | |
| 39 | 39 | const newConfig = mergeConfig({}, config); |
| 40 | 40 | |
| 41 | 41 | // Read only own properties to prevent prototype pollution gadgets |
@@ -56,8 +56,8 @@ | ||
| 56 | 56 | |
| 57 | 57 | newConfig.url = buildURL( |
| 58 | 58 | buildFullPath(baseURL, url, allowAbsoluteUrls), |
| 59 | config.params, | |
| 60 | config.paramsSerializer | |
| 59 | own('params'), | |
| 60 | own('paramsSerializer') | |
| 61 | 61 | ); |
| 62 | 62 | |
| 63 | 63 | // HTTP basic authentication |
@@ -70,8 +70,12 @@ | ||
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 72 | if (utils.isFormData(data)) { |
| 73 | if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) { | |
| 74 | headers.setContentType(undefined); // browser handles it | |
| 73 | if ( | |
| 74 | platform.hasStandardBrowserEnv || | |
| 75 | platform.hasStandardBrowserWebWorkerEnv || | |
| 76 | utils.isReactNative(data) | |
| 77 | ) { | |
| 78 | headers.setContentType(undefined); // browser/web worker/RN handles it | |
| 75 | 79 | } else if (utils.isFunction(data.getHeaders)) { |
| 76 | 80 | // Node.js FormData (like form-data package) |
| 77 | 81 | setFormDataHeaders(headers, data.getHeaders(), own('formDataHeaderPolicy')); |
@@ -103,4 +107,6 @@ | ||
| 103 | 107 | } |
| 104 | 108 | |
| 105 | 109 | return newConfig; |
| 106 | }; | |
| 110 | } | |
| 111 | ||
| 112 | export default resolveConfig; | |
@@ -412,7 +412,9 @@ | ||
| 412 | 412 | return; |
| 413 | 413 | } |
| 414 | 414 | |
| 415 | const targetKey = (caseless && findKey(result, key)) || key; | |
| 415 | // findKey lowercases the key, so caseless lookup only applies to strings — | |
| 416 | // symbol keys are identity-matched. | |
| 417 | const targetKey = (caseless && typeof key === 'string' && findKey(result, key)) || key; | |
| 416 | 418 | // Read via own-prop only — a bare `result[targetKey]` walks the prototype |
| 417 | 419 | // chain, so a polluted Object.prototype value could surface here and get |
| 418 | 420 | // copied into the merged result. |
@@ -429,7 +431,24 @@ | ||
| 429 | 431 | }; |
| 430 | 432 | |
| 431 | 433 | for (let i = 0, l = objs.length; i < l; i++) { |
| 432 | objs[i] && forEach(objs[i], assignValue); | |
| 434 | const source = objs[i]; | |
| 435 | if (!source || isBuffer(source)) { | |
| 436 | continue; | |
| 437 | } | |
| 438 | ||
| 439 | forEach(source, assignValue); | |
| 440 | ||
| 441 | if (typeof source !== 'object' || isArray(source)) { | |
| 442 | continue; | |
| 443 | } | |
| 444 | ||
| 445 | const symbols = Object.getOwnPropertySymbols(source); | |
| 446 | for (let j = 0; j < symbols.length; j++) { | |
| 447 | const symbol = symbols[j]; | |
| 448 | if (propertyIsEnumerable.call(source, symbol)) { | |
| 449 | assignValue(source[symbol], symbol); | |
| 450 | } | |
| 451 | } | |
| 433 | 452 | } |
| 434 | 453 | return result; |
| 435 | 454 | } |
@@ -658,6 +677,8 @@ | ||
| 658 | 677 | hasOwnProperty.call(obj, prop) |
| 659 | 678 | )(Object.prototype); |
| 660 | 679 | |
| 680 | const { propertyIsEnumerable } = Object.prototype; | |
| 681 | ||
| 661 | 682 | /** |
| 662 | 683 | * Determine if a value is a RegExp object |
| 663 | 684 | * |
@@ -1,6 +1,6 @@ | ||
| 1 | 1 | { |
| 2 | 2 | "name": "axios", |
| 3 | "version": "1.16.1", | |
| 3 | "version": "1.17.0", | |
| 4 | 4 | "description": "Promise based HTTP client for the browser and node.js", |
| 5 | 5 | "main": "./dist/node/axios.cjs", |
| 6 | 6 | "module": "./index.js", |
@@ -86,8 +86,8 @@ | ||
| 86 | 86 | "Justin Beckwith (https://github.com/JustinBeckwith)", |
| 87 | 87 | "Martti Laine (https://github.com/codeclown)", |
| 88 | 88 | "Xianming Zhong (https://github.com/chinesedfan)", |
| 89 | "Shaan Majid (https://github.com/shaanmajid)", | |
| 89 | 90 | "Willian Agostini (https://github.com/WillianAgostini)", |
| 90 | "Shaan Majid (https://github.com/shaanmajid)", | |
| 91 | 91 | "Remco Haszing (https://github.com/remcohaszing)", |
| 92 | 92 | "Rikki Gibson (https://github.com/RikkiGibson)" |
| 93 | 93 | ], |
@@ -97,6 +97,22 @@ | ||
| 97 | 97 | "url": "https://github.com/axios/axios/issues" |
| 98 | 98 | }, |
| 99 | 99 | "homepage": "https://axios-http.com", |
| 100 | "files": [ | |
| 101 | "index.js", | |
| 102 | "index.d.ts", | |
| 103 | "index.d.cts", | |
| 104 | "CHANGELOG.md", | |
| 105 | "MIGRATION_GUIDE.md", | |
| 106 | "lib/", | |
| 107 | "dist/axios.js", | |
| 108 | "dist/axios.min.js", | |
| 109 | "dist/axios.min.js.map", | |
| 110 | "dist/esm/axios.js", | |
| 111 | "dist/esm/axios.min.js", | |
| 112 | "dist/esm/axios.min.js.map", | |
| 113 | "dist/browser/axios.cjs", | |
| 114 | "dist/node/axios.cjs" | |
| 115 | ], | |
| 100 | 116 | "scripts": { |
| 101 | 117 | "build": "gulp clear && cross-env NODE_ENV=production rollup -c -m", |
| 102 | 118 | "version": "npm run build && git add package.json", |
@@ -128,9 +144,9 @@ | ||
| 128 | 144 | }, |
| 129 | 145 | "devDependencies": { |
| 130 | 146 | "@babel/core": "^7.29.0", |
| 131 | "@babel/preset-env": "^7.29.2", | |
| 132 | "@commitlint/cli": "^20.5.0", | |
| 133 | "@commitlint/config-conventional": "^20.5.0", | |
| 147 | "@babel/preset-env": "^7.29.5", | |
| 148 | "@commitlint/cli": "^21.0.1", | |
| 149 | "@commitlint/config-conventional": "^21.0.1", | |
| 134 | 150 | "@eslint/js": "^10.0.1", |
| 135 | 151 | "@rollup/plugin-alias": "^6.0.0", |
| 136 | 152 | "@rollup/plugin-babel": "^7.0.0", |
@@ -138,34 +154,34 @@ | ||
| 138 | 154 | "@rollup/plugin-json": "^6.1.0", |
| 139 | 155 | "@rollup/plugin-node-resolve": "^16.0.3", |
| 140 | 156 | "@rollup/plugin-terser": "^1.0.0", |
| 141 | "@vitest/browser": "^4.1.5", | |
| 142 | "@vitest/browser-playwright": "^4.1.5", | |
| 157 | "@vitest/browser": "^4.1.7", | |
| 158 | "@vitest/browser-playwright": "^4.1.7", | |
| 143 | 159 | "abortcontroller-polyfill": "^1.7.8", |
| 144 | 160 | "acorn": "^8.16.0", |
| 145 | 161 | "body-parser": "^2.2.2", |
| 146 | 162 | "chalk": "^5.6.2", |
| 147 | 163 | "cross-env": "^10.1.0", |
| 148 | 164 | "dev-null": "^0.1.1", |
| 149 | "eslint": "^10.2.1", | |
| 165 | "eslint": "^10.4.0", | |
| 150 | 166 | "express": "^5.2.1", |
| 151 | 167 | "formdata-node": "^6.0.3", |
| 152 | 168 | "formidable": "^3.5.4", |
| 153 | 169 | "fs-extra": "^11.3.4", |
| 154 | 170 | "get-stream": "^9.0.1", |
| 155 | "globals": "^17.5.0", | |
| 171 | "globals": "^17.6.0", | |
| 156 | 172 | "gulp": "^5.0.1", |
| 157 | 173 | "husky": "^9.1.7", |
| 158 | "lint-staged": "^16.4.0", | |
| 174 | "lint-staged": "^17.0.5", | |
| 159 | 175 | "minimist": "^1.2.8", |
| 160 | 176 | "multer": "^2.1.1", |
| 161 | "playwright": "^1.59.1", | |
| 177 | "playwright": "^1.60.0", | |
| 162 | 178 | "prettier": "^3.8.3", |
| 163 | "rollup": "^4.60.2", | |
| 179 | "rollup": "^4.60.4", | |
| 164 | 180 | "rollup-plugin-bundle-size": "^1.0.3", |
| 165 | 181 | "selfsigned": "^5.5.0", |
| 166 | 182 | "stream-throttle": "^0.1.3", |
| 167 | 183 | "typescript": "^5.9.3", |
| 168 | "vitest": "^4.1.5" | |
| 184 | "vitest": "^4.1.7" | |
| 169 | 185 | }, |
| 170 | 186 | "commitlint": { |
| 171 | 187 | "rules": { |
@@ -1,5 +1,47 @@ | ||
| 1 | 1 | # Changelog |
| 2 | 2 | |
| 3 | ## v1.16.1 — May 13, 2026 | |
| 4 | ||
| 5 | This release ships a defence-in-depth fix for prototype pollution in `formDataToJSON`, hardens proxy and CI workflows, restores Webpack 4 compatibility for the fetch adapter, and includes several small bug fixes and maintenance improvements. | |
| 6 | ||
| 7 | ## 🔒 Security Fixes | |
| 8 | ||
| 9 | * **Prototype Pollution Defence-in-Depth:** Hardened `formDataToJSON` against already-polluted `Object.prototype` by walking own properties only, so attacker-controlled keys inherited from a poisoned prototype cannot propagate through deserialization. (__#7413__) | |
| 10 | * **Proxy Cleartext Leak:** Fixed an issue where HTTPS request data could be transmitted in cleartext to an HTTP proxy under certain configurations. (__#10858__) | |
| 11 | * **CI Cache Removal:** Removed all GitHub Actions caches as a defence-in-depth measure against cache poisoning vectors in the build pipeline. (__#10882__) | |
| 12 | ||
| 13 | ## 🐛 Bug Fixes | |
| 14 | ||
| 15 | * **Data URI Parsing:** Updated the `fromDataURI` regex to match RFC 2397 more strictly, fixing edge cases in `data:` URL handling. (__#10829__) | |
| 16 | * **Unicode Headers:** Preserved Unicode header values when running through request interceptors, so non-ASCII header content is no longer corrupted before dispatch. (__#10850__) | |
| 17 | * **XHR Upload Progress:** Guarded against malformed `ProgressEvent` payloads emitted by some environments during XHR upload, preventing crashes when `loaded` / `total` are missing or invalid. (__#10868__) | |
| 18 | * **Webpack 4 Fetch Adapter:** Fixed an "unexpected token" error caused by syntax in the fetch adapter that Webpack 4 could not parse, restoring compatibility for legacy bundler users. (__#10864__) | |
| 19 | * **Type Definitions:** Made `parseReviver` `context.source` optional in the type definitions to align with the ES2023 specification. (__#10837__) | |
| 20 | * **URL Object Support Reverted:** Reverted the change that allowed passing a `URL` object as `config.url` (originally __#10866__) due to regressions; this support will be reintroduced in a later release once the underlying issues are addressed. (__#10874__) | |
| 21 | ||
| 22 | ## 🔧 Maintenance & Chores | |
| 23 | ||
| 24 | * **Cycle Detection Refactor:** Replaced the array-based cycle tracker in `toJSONObject` with a `WeakSet`, improving performance and memory behaviour on large nested structures. (__#10832__) | |
| 25 | * **composeSignals Cleanup:** Refactored `composeSignals` to use a clearer early-return structure, simplifying the cancellation/abort composition path. (__#10844__) | |
| 26 | * **AI Readiness & Repo Docs:** Added `AGENTS.md` and related contributor-guide updates for both human and AI agents, plus post-release documentation improvements. (__#10835__, __#10841__) | |
| 27 | * **Docs Improvements:** Clarified the GET request example, fixed the interceptor `eject` example to reference the correct instance, and corrected the Buzzoid sponsor description in the README. (__#10836__, __#10853__, __#10856__) | |
| 28 | * **Sponsorship Tooling:** Fixed empty sponsor arrays in the sponsor processing script, added the ability to inject additional sponsors, updated the sponsorship link, and added a Twicsy advertisement entry. (__#10843__, __#10859__, __#10869__) | |
| 29 | * **Dependencies:** Bumped `@commitlint/cli` from 20.5.0 to 20.5.2. (__#10846__) | |
| 30 | ||
| 31 | ## 🌟 New Contributors | |
| 32 | ||
| 33 | We are thrilled to welcome our new contributors. Thank you for helping improve axios: | |
| 34 | ||
| 35 | * __@hpinmetaverse__ (__#10836__) | |
| 36 | * __@tommyhgunz14__ (__#7413__) | |
| 37 | * __@abhu85__ (__#10829__) | |
| 38 | * __@divyanshuraj1095__ (__#10853__) | |
| 39 | * __@sagodi97__ (__#10856__) | |
| 40 | * __@rkdfx__ (__#10868__) | |
| 41 | * __@Liuwei1125__ (__#10866__) | |
| 42 | ||
| 43 | [Full Changelog](https://github.com/axios/axios/compare/v1.16.0...v1.16.1) | |
| 44 | ||
| 3 | 45 | ## v1.16.0 — May 2, 2026 |
| 4 | 46 | |
| 5 | 47 | 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. |
@@ -47,7 +47,9 @@ | ||
| 47 | 47 | ...targets: Array<AxiosHeaders | RawAxiosHeaders | string | undefined | null> |
| 48 | 48 | ): AxiosHeaders; |
| 49 | 49 | |
| 50 | toJSON(asStrings?: boolean): RawAxiosHeaders; | |
| 50 | toJSON(asStrings: true): Record<string, string>; | |
| 51 | toJSON(asStrings?: false): Record<string, string | string[]>; | |
| 52 | toJSON(asStrings?: boolean): Record<string, string | string[]>; | |
| 51 | 53 | |
| 52 | 54 | static from(thing?: AxiosHeaders | RawAxiosHeaders | string): AxiosHeaders; |
| 53 | 55 | |
@@ -281,6 +283,7 @@ | ||
| 281 | 283 | forcedJSONParsing?: boolean; |
| 282 | 284 | clarifyTimeoutError?: boolean; |
| 283 | 285 | legacyInterceptorReqResOrdering?: boolean; |
| 286 | advertiseZstdAcceptEncoding?: boolean; | |
| 284 | 287 | } |
| 285 | 288 | |
| 286 | 289 | export interface GenericAbortSignal { |
@@ -0,0 +1,119 @@ | ||
| 1 | 'use strict'; | |
| 2 | ||
| 3 | // Node-only: relies on the built-in `http2` module. Browser/react-native | |
| 4 | // builds replace `lib/adapters/http.js` (the sole importer) with `lib/helpers/null.js` | |
| 5 | // via the `browser` package.json field, so this module is never reached in | |
| 6 | // those environments. Do not import it from any browser-reachable code path. | |
| 7 | ||
| 8 | import http2 from 'http2'; | |
| 9 | import util from 'util'; | |
| 10 | ||
| 11 | class Http2Sessions { | |
| 12 | constructor() { | |
| 13 | this.sessions = Object.create(null); | |
| 14 | } | |
| 15 | ||
| 16 | getSession(authority, options) { | |
| 17 | options = Object.assign( | |
| 18 | { | |
| 19 | sessionTimeout: 1000, | |
| 20 | }, | |
| 21 | options | |
| 22 | ); | |
| 23 | ||
| 24 | let authoritySessions = this.sessions[authority]; | |
| 25 | ||
| 26 | if (authoritySessions) { | |
| 27 | let len = authoritySessions.length; | |
| 28 | ||
| 29 | for (let i = 0; i < len; i++) { | |
| 30 | const [sessionHandle, sessionOptions] = authoritySessions[i]; | |
| 31 | if ( | |
| 32 | !sessionHandle.destroyed && | |
| 33 | !sessionHandle.closed && | |
| 34 | util.isDeepStrictEqual(sessionOptions, options) | |
| 35 | ) { | |
| 36 | return sessionHandle; | |
| 37 | } | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | const session = http2.connect(authority, options); | |
| 42 | ||
| 43 | let removed; | |
| 44 | let timer; | |
| 45 | ||
| 46 | const removeSession = () => { | |
| 47 | if (removed) { | |
| 48 | return; | |
| 49 | } | |
| 50 | ||
| 51 | removed = true; | |
| 52 | ||
| 53 | if (timer) { | |
| 54 | clearTimeout(timer); | |
| 55 | timer = null; | |
| 56 | } | |
| 57 | ||
| 58 | let entries = authoritySessions, | |
| 59 | len = entries.length, | |
| 60 | i = len; | |
| 61 | ||
| 62 | while (i--) { | |
| 63 | if (entries[i][0] === session) { | |
| 64 | if (len === 1) { | |
| 65 | delete this.sessions[authority]; | |
| 66 | } else { | |
| 67 | entries.splice(i, 1); | |
| 68 | } | |
| 69 | if (!session.closed) { | |
| 70 | session.close(); | |
| 71 | } | |
| 72 | return; | |
| 73 | } | |
| 74 | } | |
| 75 | }; | |
| 76 | ||
| 77 | const originalRequestFn = session.request; | |
| 78 | ||
| 79 | const { sessionTimeout } = options; | |
| 80 | ||
| 81 | if (sessionTimeout != null) { | |
| 82 | let streamsCount = 0; | |
| 83 | ||
| 84 | session.request = function () { | |
| 85 | const stream = originalRequestFn.apply(this, arguments); | |
| 86 | ||
| 87 | streamsCount++; | |
| 88 | ||
| 89 | if (timer) { | |
| 90 | clearTimeout(timer); | |
| 91 | timer = null; | |
| 92 | } | |
| 93 | ||
| 94 | stream.once('close', () => { | |
| 95 | if (!--streamsCount) { | |
| 96 | timer = setTimeout(() => { | |
| 97 | timer = null; | |
| 98 | removeSession(); | |
| 99 | }, sessionTimeout); | |
| 100 | } | |
| 101 | }); | |
| 102 | ||
| 103 | return stream; | |
| 104 | }; | |
| 105 | } | |
| 106 | ||
| 107 | session.once('close', removeSession); | |
| 108 | ||
| 109 | let entry = [session, options]; | |
| 110 | ||
| 111 | authoritySessions | |
| 112 | ? authoritySessions.push(entry) | |
| 113 | : (authoritySessions = this.sessions[authority] = [entry]); | |
| 114 | ||
| 115 | return session; | |
| 116 | } | |
| 117 | } | |
| 118 | ||
| 119 | export default Http2Sessions; | |