Compare commits

..

No commits in common. "v1.x" and "v1.15.0" have entirely different histories.

17 changed files with 319 additions and 523 deletions

View File

@ -1,5 +1,5 @@
<h3 align="center"> 💎 Platinum sponsors <br> </h3> <table align="center"><tr><td align="center" width="50%"> <a href="https://thanks.dev/?utm_source&#x3D;axios&amp;utm_medium&#x3D;sponsorlist&amp;utm_campaign&#x3D;sponsorship" style="padding: 10px; display: inline-block" target="_blank"> <img width="90px" height="90px" src="https://axios-http.com/assets/sponsors/opencollective/ed51c2ee8f1b70aa3484d6dd678652134079a036.png" alt="THANKS.DEV"/> </a> <p align="center" title="We&#x27;re passionate about making open source sustainable. Scan your dependancy tree to better understand which open source projects need funding the most. Maintainers can also register their projects to become eligible for funding.">We&#x27;re passionate about making open source sustainable. Scan your dependancy tree to better understand which open source projects need funding the...</p> <p align="center"> <a href="https://thanks.dev/?utm_source&#x3D;axios&amp;utm_medium&#x3D;readme_sponsorlist&amp;utm_campaign&#x3D;sponsorship" target="_blank"><b>thanks.dev</b></a> </p>
</td><td align="center" width="50%"> <a href="https://opencollective.com/hopper-security?utm_source&#x3D;axios&amp;utm_medium&#x3D;sponsorlist&amp;utm_campaign&#x3D;sponsorship" style="padding: 10px; display: inline-block" target="_blank"> <img width="90px" height="90px" src="https://axios-http.com/assets/sponsors/opencollective/180d02a83ee99448f850e39eed6dbb95f56000ba.png" alt="Hopper Security"/> </a> <p align="center">Hopper provides a secure open-source registry where every component is verified against malware and continuously remediated for vulnerabilities across any version. In simple terms, Hopper removes the need to manage software supply chain risk altogether.</p><p align="center"> <a href="https://hopper.security/?utm_source&#x3D;axios&amp;utm_medium&#x3D;readme_sponsorlist&amp;utm_campaign&#x3D;sponsorship" target="_blank"><b>Hopper.Security</b></a> </p>
</td><td align="center" width="50%"> <a href="https://opencollective.com/hopper-security?utm_source&#x3D;axios&amp;utm_medium&#x3D;sponsorlist&amp;utm_campaign&#x3D;sponsorship" style="padding: 10px; display: inline-block" target="_blank"> <img width="90px" height="90px" src="https://axios-http.com/assets/sponsors/opencollective/180d02a83ee99448f850e39eed6dbb95f56000ba.png" alt="Hopper Security"/> </a> <p align="center"> </p>
</td></tr></table><table align="center"><tr><td align="center" width="50%"> <a href="https://opencollective.com/axios/contribute" target="_blank" >💜 Become a sponsor</a>
</td><td align="center" width="50%"> <a href="https://opencollective.com/axios/contribute" target="_blank" >💜 Become a sponsor</a>
</td></tr></table>

View File

@ -20,8 +20,7 @@ type CommonRequestHeadersList =
| 'Content-Length'
| 'User-Agent'
| 'Content-Encoding'
| 'Authorization'
| 'Location';
| 'Authorization';
type ContentType =
| axios.AxiosHeaderValue
@ -39,8 +38,6 @@ type CommonResponseHeadersList =
| 'Cache-Control'
| 'Content-Encoding';
type CommonResponseHeaderKey = CommonResponseHeadersList | Lowercase<CommonResponseHeadersList>;
type BrowserProgressEvent = any;
declare class AxiosHeaders {
@ -309,7 +306,7 @@ declare namespace axios {
type AxiosHeaderValue = AxiosHeaders | string | string[] | number | boolean | null;
type RawCommonResponseHeaders = {
[Key in CommonResponseHeaderKey]: AxiosHeaderValue;
[Key in CommonResponseHeadersList]: AxiosHeaderValue;
} & {
'set-cookie': string[];
};
@ -347,38 +344,56 @@ declare namespace axios {
protocol?: string;
}
type UppercaseMethod =
type Method =
| 'get'
| 'GET'
| 'delete'
| 'DELETE'
| 'head'
| 'HEAD'
| 'options'
| 'OPTIONS'
| 'post'
| 'POST'
| 'put'
| 'PUT'
| 'patch'
| 'PATCH'
| 'purge'
| 'PURGE'
| 'link'
| 'LINK'
| 'unlink'
| 'UNLINK';
type Method = (UppercaseMethod | Lowercase<UppercaseMethod>) & {};
type ResponseType = 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' | 'stream' | 'formdata';
type UppercaseResponseEncoding =
type responseEncoding =
| 'ascii'
| 'ASCII'
| 'ansi'
| 'ANSI'
| 'binary'
| 'BINARY'
| 'base64'
| 'BASE64'
| 'base64url'
| 'BASE64URL'
| 'hex'
| 'HEX'
| 'latin1'
| 'LATIN1'
| 'ucs-2'
| 'UCS-2'
| 'ucs2'
| 'UCS2'
| 'utf-8'
| 'UTF-8'
| 'utf8'
| 'UTF8'
| 'utf16le'
| 'UTF16LE';
type responseEncoding = (UppercaseResponseEncoding | Lowercase<UppercaseResponseEncoding>) & {};
interface TransitionalOptions {
silentJSONParsing?: boolean;
forcedJSONParsing?: boolean;
@ -613,7 +628,7 @@ declare namespace axios {
interface AxiosInterceptorOptions {
synchronous?: boolean;
runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null;
runWhen?: (config: InternalAxiosRequestConfig) => boolean;
}
type AxiosInterceptorFulfilled<T> = (value: T) => T | Promise<T>;
@ -634,7 +649,7 @@ declare namespace axios {
fulfilled: AxiosInterceptorFulfilled<T>;
rejected?: AxiosInterceptorRejected;
synchronous: boolean;
runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null;
runWhen?: (config: AxiosRequestConfig) => boolean;
}
interface AxiosInterceptorManager<V> {

355
index.d.ts vendored
View File

@ -1,7 +1,13 @@
// TypeScript Version: 4.7
type StringLiteralsOrString<Literals extends string> = Literals | (string & {});
export type AxiosHeaderValue = AxiosHeaders | string | string[] | number | boolean | null;
export type AxiosHeaderValue =
| AxiosHeaders
| string
| string[]
| number
| boolean
| null;
interface RawAxiosHeaders {
[key: string]: AxiosHeaderValue;
@ -18,7 +24,11 @@ type AxiosHeaderMatcher =
| RegExp
| ((this: AxiosHeaders, value: string, name: string) => boolean);
type AxiosHeaderParser = (this: AxiosHeaders, value: AxiosHeaderValue, header: string) => any;
type AxiosHeaderParser = (
this: AxiosHeaders,
value: AxiosHeaderValue,
header: string,
) => any;
export class AxiosHeaders {
constructor(headers?: RawAxiosHeaders | AxiosHeaders | string);
@ -28,9 +38,12 @@ export class AxiosHeaders {
set(
headerName?: string,
value?: AxiosHeaderValue,
rewrite?: boolean | AxiosHeaderMatcher
rewrite?: boolean | AxiosHeaderMatcher,
): AxiosHeaders;
set(
headers?: RawAxiosHeaders | AxiosHeaders | string,
rewrite?: boolean,
): AxiosHeaders;
set(headers?: RawAxiosHeaders | AxiosHeaders | string, rewrite?: boolean): AxiosHeaders;
get(headerName: string, parser: RegExp): RegExpExecArray | null;
get(headerName: string, matcher?: true | AxiosHeaderParser): AxiosHeaderValue;
@ -44,7 +57,9 @@ export class AxiosHeaders {
normalize(format: boolean): AxiosHeaders;
concat(
...targets: Array<AxiosHeaders | RawAxiosHeaders | string | undefined | null>
...targets: Array<
AxiosHeaders | RawAxiosHeaders | string | undefined | null
>
): AxiosHeaders;
toJSON(asStrings?: boolean): RawAxiosHeaders;
@ -54,35 +69,55 @@ export class AxiosHeaders {
static accessor(header: string | string[]): AxiosHeaders;
static concat(
...targets: Array<AxiosHeaders | RawAxiosHeaders | string | undefined | null>
...targets: Array<
AxiosHeaders | RawAxiosHeaders | string | undefined | null
>
): AxiosHeaders;
setContentType(value: ContentType, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;
setContentType(
value: ContentType,
rewrite?: boolean | AxiosHeaderMatcher,
): AxiosHeaders;
getContentType(parser?: RegExp): RegExpExecArray | null;
getContentType(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;
hasContentType(matcher?: AxiosHeaderMatcher): boolean;
setContentLength(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;
setContentLength(
value: AxiosHeaderValue,
rewrite?: boolean | AxiosHeaderMatcher,
): AxiosHeaders;
getContentLength(parser?: RegExp): RegExpExecArray | null;
getContentLength(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;
hasContentLength(matcher?: AxiosHeaderMatcher): boolean;
setAccept(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;
setAccept(
value: AxiosHeaderValue,
rewrite?: boolean | AxiosHeaderMatcher,
): AxiosHeaders;
getAccept(parser?: RegExp): RegExpExecArray | null;
getAccept(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;
hasAccept(matcher?: AxiosHeaderMatcher): boolean;
setUserAgent(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;
setUserAgent(
value: AxiosHeaderValue,
rewrite?: boolean | AxiosHeaderMatcher,
): AxiosHeaders;
getUserAgent(parser?: RegExp): RegExpExecArray | null;
getUserAgent(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;
hasUserAgent(matcher?: AxiosHeaderMatcher): boolean;
setContentEncoding(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;
setContentEncoding(
value: AxiosHeaderValue,
rewrite?: boolean | AxiosHeaderMatcher,
): AxiosHeaders;
getContentEncoding(parser?: RegExp): RegExpExecArray | null;
getContentEncoding(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;
hasContentEncoding(matcher?: AxiosHeaderMatcher): boolean;
setAuthorization(value: AxiosHeaderValue, rewrite?: boolean | AxiosHeaderMatcher): AxiosHeaders;
setAuthorization(
value: AxiosHeaderValue,
rewrite?: boolean | AxiosHeaderMatcher,
): AxiosHeaders;
getAuthorization(parser?: RegExp): RegExpExecArray | null;
getAuthorization(matcher?: AxiosHeaderMatcher): AxiosHeaderValue;
hasAuthorization(matcher?: AxiosHeaderMatcher): boolean;
@ -93,53 +128,56 @@ export class AxiosHeaders {
}
type CommonRequestHeadersList =
| 'Accept'
| 'Content-Length'
| 'User-Agent'
| 'Content-Encoding'
| 'Authorization'
| 'Location';
| "Accept"
| "Content-Length"
| "User-Agent"
| "Content-Encoding"
| "Authorization";
type ContentType =
| AxiosHeaderValue
| 'text/html'
| 'text/plain'
| 'multipart/form-data'
| 'application/json'
| 'application/x-www-form-urlencoded'
| 'application/octet-stream';
| "text/html"
| "text/plain"
| "multipart/form-data"
| "application/json"
| "application/x-www-form-urlencoded"
| "application/octet-stream";
export type RawAxiosRequestHeaders = Partial<
RawAxiosHeaders & {
[Key in CommonRequestHeadersList]: AxiosHeaderValue;
} & {
'Content-Type': ContentType;
"Content-Type": ContentType;
}
>;
export type AxiosRequestHeaders = RawAxiosRequestHeaders & AxiosHeaders;
type CommonResponseHeadersList =
| 'Server'
| 'Content-Type'
| 'Content-Length'
| 'Cache-Control'
| 'Content-Encoding';
type CommonResponseHeaderKey = CommonResponseHeadersList | Lowercase<CommonResponseHeadersList>;
| "Server"
| "Content-Type"
| "Content-Length"
| "Cache-Control"
| "Content-Encoding";
type RawCommonResponseHeaders = {
[Key in CommonResponseHeaderKey]: AxiosHeaderValue;
[Key in CommonResponseHeadersList]: AxiosHeaderValue;
} & {
'set-cookie': string[];
"set-cookie": string[];
};
export type RawAxiosResponseHeaders = Partial<RawAxiosHeaders & RawCommonResponseHeaders>;
export type RawAxiosResponseHeaders = Partial<
RawAxiosHeaders & RawCommonResponseHeaders
>;
export type AxiosResponseHeaders = RawAxiosResponseHeaders & AxiosHeaders;
export interface AxiosRequestTransformer {
(this: InternalAxiosRequestConfig, data: any, headers: AxiosRequestHeaders): any;
(
this: InternalAxiosRequestConfig,
data: any,
headers: AxiosRequestHeaders,
): any;
}
export interface AxiosResponseTransformer {
@ -147,7 +185,7 @@ export interface AxiosResponseTransformer {
this: InternalAxiosRequestConfig,
data: any,
headers: AxiosResponseHeaders,
status?: number
status?: number,
): any;
}
@ -233,47 +271,62 @@ export enum HttpStatusCode {
NetworkAuthenticationRequired = 511,
}
type UppercaseMethod =
| 'GET'
| 'DELETE'
| 'HEAD'
| 'OPTIONS'
| 'POST'
| 'PUT'
| 'PATCH'
| 'PURGE'
| 'LINK'
| 'UNLINK';
export type Method = (UppercaseMethod | Lowercase<UppercaseMethod>) & {};
export type Method =
| "get"
| "GET"
| "delete"
| "DELETE"
| "head"
| "HEAD"
| "options"
| "OPTIONS"
| "post"
| "POST"
| "put"
| "PUT"
| "patch"
| "PATCH"
| "purge"
| "PURGE"
| "link"
| "LINK"
| "unlink"
| "UNLINK";
export type ResponseType =
| 'arraybuffer'
| 'blob'
| 'document'
| 'json'
| 'text'
| 'stream'
| 'formdata';
| "arraybuffer"
| "blob"
| "document"
| "json"
| "text"
| "stream"
| "formdata";
type UppercaseResponseEncoding =
| 'ASCII'
| 'ANSI'
| 'BINARY'
| 'BASE64'
| 'BASE64URL'
| 'HEX'
| 'LATIN1'
| 'UCS-2'
| 'UCS2'
| 'UTF-8'
| 'UTF8'
| 'UTF16LE';
export type responseEncoding = (
| UppercaseResponseEncoding
| Lowercase<UppercaseResponseEncoding>
) & {};
export type responseEncoding =
| "ascii"
| "ASCII"
| "ansi"
| "ANSI"
| "binary"
| "BINARY"
| "base64"
| "BASE64"
| "base64url"
| "BASE64URL"
| "hex"
| "HEX"
| "latin1"
| "LATIN1"
| "ucs-2"
| "UCS-2"
| "ucs2"
| "UCS2"
| "utf-8"
| "UTF-8"
| "utf8"
| "UTF8"
| "utf16le"
| "UTF16LE";
export interface TransitionalOptions {
silentJSONParsing?: boolean;
@ -301,7 +354,7 @@ export interface SerializerVisitor {
value: any,
key: string | number,
path: null | Array<string | number>,
helpers: FormDataVisitorHelpers
helpers: FormDataVisitorHelpers,
): boolean;
}
@ -349,7 +402,7 @@ export interface AxiosProgressEvent {
type Milliseconds = number;
type AxiosAdapterName = StringLiteralsOrString<'xhr' | 'http' | 'fetch'>;
type AxiosAdapterName = StringLiteralsOrString<"xhr" | "http" | "fetch">;
type AxiosAdapterConfig = AxiosAdapter | AxiosAdapterName;
@ -394,7 +447,7 @@ export interface AxiosRequestConfig<D = any> {
responseDetails: {
headers: Record<string, string>;
statusCode: HttpStatusCode;
}
},
) => void;
socketPath?: string | null;
transport?: any;
@ -408,11 +461,24 @@ export interface AxiosRequestConfig<D = any> {
insecureHTTPParser?: boolean;
env?: {
FormData?: new (...args: any[]) => object;
fetch?: (input: URL | Request | string, init?: RequestInit) => Promise<Response>;
Request?: new (input: URL | Request | string, init?: RequestInit) => Request;
fetch?: (
input: URL | Request | string,
init?: RequestInit,
) => Promise<Response>;
Request?: new (
input: URL | Request | string,
init?: RequestInit,
) => Request;
Response?: new (
body?: ArrayBuffer | ArrayBufferView | Blob | FormData | URLSearchParams | string | null,
init?: ResponseInit
body?:
| ArrayBuffer
| ArrayBufferView
| Blob
| FormData
| URLSearchParams
| string
| null,
init?: ResponseInit,
) => Response;
};
formSerializer?: FormSerializerOptions;
@ -424,18 +490,26 @@ export interface AxiosRequestConfig<D = any> {
cb: (
err: Error | null,
address: LookupAddress | LookupAddress[],
family?: AddressFamily
) => void
family?: AddressFamily,
) => void,
) => void)
| ((
hostname: string,
options: object
options: object,
) => Promise<
[address: LookupAddressEntry | LookupAddressEntry[], family?: AddressFamily] | LookupAddress
| [
address: LookupAddressEntry | LookupAddressEntry[],
family?: AddressFamily,
]
| LookupAddress
>);
withXSRFToken?: boolean | ((config: InternalAxiosRequestConfig) => boolean | undefined);
withXSRFToken?:
| boolean
| ((config: InternalAxiosRequestConfig) => boolean | undefined);
parseReviver?: (this: any, key: string, value: any) => any;
fetchOptions?: Omit<RequestInit, 'body' | 'headers' | 'method' | 'signal'> | Record<string, any>;
fetchOptions?:
| Omit<RequestInit, "body" | "headers" | "method" | "signal">
| Record<string, any>;
httpVersion?: 1 | 2;
http2Options?: Record<string, any> & {
sessionTimeout?: number;
@ -445,7 +519,9 @@ export interface AxiosRequestConfig<D = any> {
// Alias
export type RawAxiosRequestConfig<D = any> = AxiosRequestConfig<D>;
export interface InternalAxiosRequestConfig<D = any> extends AxiosRequestConfig<D> {
export interface InternalAxiosRequestConfig<
D = any,
> extends AxiosRequestConfig<D> {
headers: AxiosRequestHeaders;
}
@ -463,11 +539,17 @@ export interface HeadersDefaults {
unlink?: RawAxiosRequestHeaders;
}
export interface AxiosDefaults<D = any> extends Omit<AxiosRequestConfig<D>, 'headers'> {
export interface AxiosDefaults<D = any> extends Omit<
AxiosRequestConfig<D>,
"headers"
> {
headers: HeadersDefaults;
}
export interface CreateAxiosDefaults<D = any> extends Omit<AxiosRequestConfig<D>, 'headers'> {
export interface CreateAxiosDefaults<D = any> extends Omit<
AxiosRequestConfig<D>,
"headers"
> {
headers?: RawAxiosRequestHeaders | AxiosHeaders | Partial<HeadersDefaults>;
}
@ -486,7 +568,7 @@ export class AxiosError<T = unknown, D = any> extends Error {
code?: string,
config?: InternalAxiosRequestConfig<D>,
request?: any,
response?: AxiosResponse<T, D>
response?: AxiosResponse<T, D>,
);
config?: InternalAxiosRequestConfig<D>;
@ -504,24 +586,24 @@ export class AxiosError<T = unknown, D = any> extends Error {
config?: InternalAxiosRequestConfig<D>,
request?: any,
response?: AxiosResponse<T, D>,
customProps?: object
customProps?: object,
): AxiosError<T, D>;
static readonly ERR_FR_TOO_MANY_REDIRECTS = 'ERR_FR_TOO_MANY_REDIRECTS';
static readonly ERR_BAD_OPTION_VALUE = 'ERR_BAD_OPTION_VALUE';
static readonly ERR_BAD_OPTION = 'ERR_BAD_OPTION';
static readonly ERR_NETWORK = 'ERR_NETWORK';
static readonly ERR_DEPRECATED = 'ERR_DEPRECATED';
static readonly ERR_BAD_RESPONSE = 'ERR_BAD_RESPONSE';
static readonly ERR_BAD_REQUEST = 'ERR_BAD_REQUEST';
static readonly ERR_NOT_SUPPORT = 'ERR_NOT_SUPPORT';
static readonly ERR_INVALID_URL = 'ERR_INVALID_URL';
static readonly ERR_CANCELED = 'ERR_CANCELED';
static readonly ECONNABORTED = 'ECONNABORTED';
static readonly ETIMEDOUT = 'ETIMEDOUT';
static readonly ERR_FR_TOO_MANY_REDIRECTS = "ERR_FR_TOO_MANY_REDIRECTS";
static readonly ERR_BAD_OPTION_VALUE = "ERR_BAD_OPTION_VALUE";
static readonly ERR_BAD_OPTION = "ERR_BAD_OPTION";
static readonly ERR_NETWORK = "ERR_NETWORK";
static readonly ERR_DEPRECATED = "ERR_DEPRECATED";
static readonly ERR_BAD_RESPONSE = "ERR_BAD_RESPONSE";
static readonly ERR_BAD_REQUEST = "ERR_BAD_REQUEST";
static readonly ERR_NOT_SUPPORT = "ERR_NOT_SUPPORT";
static readonly ERR_INVALID_URL = "ERR_INVALID_URL";
static readonly ERR_CANCELED = "ERR_CANCELED";
static readonly ECONNABORTED = "ECONNABORTED";
static readonly ETIMEDOUT = "ETIMEDOUT";
}
export class CanceledError<T> extends AxiosError<T> {
readonly name: 'CanceledError';
readonly name: "CanceledError";
}
export type AxiosPromise<T = any> = Promise<AxiosResponse<T>>;
@ -556,7 +638,7 @@ export interface CancelTokenSource {
export interface AxiosInterceptorOptions {
synchronous?: boolean;
runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null;
runWhen?: (config: InternalAxiosRequestConfig) => boolean;
}
type AxiosInterceptorFulfilled<T> = (value: T) => T | Promise<T>;
@ -565,23 +647,25 @@ type AxiosInterceptorRejected = (error: any) => any;
type AxiosRequestInterceptorUse<T> = (
onFulfilled?: AxiosInterceptorFulfilled<T> | null,
onRejected?: AxiosInterceptorRejected | null,
options?: AxiosInterceptorOptions
options?: AxiosInterceptorOptions,
) => number;
type AxiosResponseInterceptorUse<T> = (
onFulfilled?: AxiosInterceptorFulfilled<T> | null,
onRejected?: AxiosInterceptorRejected | null
onRejected?: AxiosInterceptorRejected | null,
) => number;
interface AxiosInterceptorHandler<T> {
fulfilled: AxiosInterceptorFulfilled<T>;
rejected?: AxiosInterceptorRejected;
synchronous: boolean;
runWhen?: ((config: InternalAxiosRequestConfig) => boolean) | null;
runWhen: (config: AxiosRequestConfig) => boolean | null;
}
export interface AxiosInterceptorManager<V> {
use: V extends AxiosResponse ? AxiosResponseInterceptorUse<V> : AxiosRequestInterceptorUse<V>;
use: V extends AxiosResponse
? AxiosResponseInterceptorUse<V>
: AxiosRequestInterceptorUse<V>;
eject(id: number): void;
clear(): void;
handlers?: Array<AxiosInterceptorHandler<V>>;
@ -595,61 +679,68 @@ export class Axios {
response: AxiosInterceptorManager<AxiosResponse>;
};
getUri(config?: AxiosRequestConfig): string;
request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
request<T = any, R = AxiosResponse<T>, D = any>(
config: AxiosRequestConfig<D>,
): Promise<R>;
get<T = any, R = AxiosResponse<T>, D = any>(
url: string,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
delete<T = any, R = AxiosResponse<T>, D = any>(
url: string,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
head<T = any, R = AxiosResponse<T>, D = any>(
url: string,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
options<T = any, R = AxiosResponse<T>, D = any>(
url: string,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
post<T = any, R = AxiosResponse<T>, D = any>(
url: string,
data?: D,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
put<T = any, R = AxiosResponse<T>, D = any>(
url: string,
data?: D,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
patch<T = any, R = AxiosResponse<T>, D = any>(
url: string,
data?: D,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
postForm<T = any, R = AxiosResponse<T>, D = any>(
url: string,
data?: D,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
putForm<T = any, R = AxiosResponse<T>, D = any>(
url: string,
data?: D,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
patchForm<T = any, R = AxiosResponse<T>, D = any>(
url: string,
data?: D,
config?: AxiosRequestConfig<D>
config?: AxiosRequestConfig<D>,
): Promise<R>;
}
export interface AxiosInstance extends Axios {
<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D>): Promise<R>;
<T = any, R = AxiosResponse<T>, D = any>(url: string, config?: AxiosRequestConfig<D>): Promise<R>;
<T = any, R = AxiosResponse<T>, D = any>(
config: AxiosRequestConfig<D>,
): Promise<R>;
<T = any, R = AxiosResponse<T>, D = any>(
url: string,
config?: AxiosRequestConfig<D>,
): Promise<R>;
create(config?: CreateAxiosDefaults): AxiosInstance;
defaults: Omit<AxiosDefaults, 'headers'> & {
defaults: Omit<AxiosDefaults, "headers"> & {
headers: HeadersDefaults & {
[key: string]: AxiosHeaderValue;
};
@ -667,18 +758,22 @@ export interface GenericHTMLFormElement {
}
export function getAdapter(
adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined
adapters: AxiosAdapterConfig | AxiosAdapterConfig[] | undefined,
): AxiosAdapter;
export function toFormData(
sourceObj: object,
targetFormData?: GenericFormData,
options?: FormSerializerOptions
options?: FormSerializerOptions,
): GenericFormData;
export function formToJSON(form: GenericFormData | GenericHTMLFormElement): object;
export function formToJSON(
form: GenericFormData | GenericHTMLFormElement,
): object;
export function isAxiosError<T = any, D = any>(payload: any): payload is AxiosError<T, D>;
export function isAxiosError<T = any, D = any>(
payload: any,
): payload is AxiosError<T, D>;
export function spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R;
@ -688,7 +783,7 @@ export function all<T>(values: Array<T | Promise<T>>): Promise<T[]>;
export function mergeConfig<D = any>(
config1: AxiosRequestConfig<D>,
config2: AxiosRequestConfig<D>
config2: AxiosRequestConfig<D>,
): AxiosRequestConfig<D>;
export interface AxiosStatic extends AxiosInstance {

View File

@ -221,19 +221,6 @@ const factory = (env) => {
// see https://github.com/cloudflare/workerd/issues/902
const isCredentialsSupported = isRequestSupported && 'credentials' in Request.prototype;
// If data is FormData and Content-Type is multipart/form-data without boundary,
// delete it so fetch can set it correctly with the boundary
if (utils.isFormData(data)) {
const contentType = headers.getContentType();
if (
contentType &&
/^multipart\/form-data/i.test(contentType) &&
!/boundary=/i.test(contentType)
) {
headers.delete('content-type');
}
}
const resolvedOptions = {
...fetchOptions,
signal: composedSignal,

View File

@ -875,21 +875,6 @@ export default isHttpAdapterSupported &&
req.on('socket', function handleRequestSocket(socket) {
// default interval of sending ack packet is 1 minute
socket.setKeepAlive(true, 1000 * 60);
const removeSocketErrorListener = () => {
socket.removeListener('error', handleRequestSocketError);
};
function handleRequestSocketError(err) {
removeSocketErrorListener();
if (!req.destroyed) {
req.destroy(err);
}
}
socket.on('error', handleRequestSocketError);
req.once('close', removeSocketErrorListener);
});
// Handle request timeout

View File

@ -5,41 +5,41 @@ import parseHeaders from '../helpers/parseHeaders.js';
const $internals = Symbol('internals');
const INVALID_HEADER_VALUE_CHARS_RE = /[^\x09\x20-\x7E\x80-\xFF]/g;
const isValidHeaderValue = (value) => !/[\r\n]/.test(value);
function trimSPorHTAB(str) {
let start = 0;
let end = str.length;
while (start < end) {
const code = str.charCodeAt(start);
if (code !== 0x09 && code !== 0x20) {
break;
}
start += 1;
function assertValidHeaderValue(value, header) {
if (value === false || value == null) {
return;
}
while (end > start) {
const code = str.charCodeAt(end - 1);
if (code !== 0x09 && code !== 0x20) {
break;
}
end -= 1;
if (utils.isArray(value)) {
value.forEach((v) => assertValidHeaderValue(v, header));
return;
}
return start === 0 && end === str.length ? str : str.slice(start, end);
if (!isValidHeaderValue(String(value))) {
throw new Error(`Invalid character in header content ["${header}"]`);
}
}
function normalizeHeader(header) {
return header && String(header).trim().toLowerCase();
}
function sanitizeHeaderValue(str) {
return trimSPorHTAB(str.replace(INVALID_HEADER_VALUE_CHARS_RE, ''));
function stripTrailingCRLF(str) {
let end = str.length;
while (end > 0) {
const charCode = str.charCodeAt(end - 1);
if (charCode !== 10 && charCode !== 13) {
break;
}
end -= 1;
}
return end === str.length ? str : str.slice(0, end);
}
function normalizeValue(value) {
@ -47,7 +47,7 @@ function normalizeValue(value) {
return value;
}
return utils.isArray(value) ? value.map(normalizeValue) : sanitizeHeaderValue(String(value));
return utils.isArray(value) ? value.map(normalizeValue) : stripTrailingCRLF(String(value));
}
function parseTokens(str) {
@ -129,6 +129,7 @@ class AxiosHeaders {
_rewrite === true ||
(_rewrite === undefined && self[key] !== false)
) {
assertValidHeaderValue(_value, _header);
self[key || _header] = normalizeValue(_value);
}
}

View File

@ -15,7 +15,7 @@ import combineURLs from '../helpers/combineURLs.js';
*/
export default function buildFullPath(baseURL, requestedURL, allowAbsoluteUrls) {
let isRelativeUrl = !isAbsoluteURL(requestedURL);
if (baseURL && (isRelativeUrl || allowAbsoluteUrls === false)) {
if (baseURL && (isRelativeUrl || allowAbsoluteUrls == false)) {
return combineURLs(baseURL, requestedURL);
}
return requestedURL;

View File

@ -58,9 +58,7 @@ function formDataToJSON(formData) {
if (isLast) {
if (utils.hasOwnProp(target, name)) {
target[name] = utils.isArray(target[name])
? target[name].concat(value)
: [target[name], value];
target[name] = [target[name], value];
} else {
target[name] = value;
}

View File

@ -7,13 +7,13 @@ export const progressEventReducer = (listener, isDownloadStream, freq = 3) => {
const _speedometer = speedometer(50, 250);
return throttle((e) => {
const rawLoaded = e.loaded;
const loaded = e.loaded;
const total = e.lengthComputable ? e.total : undefined;
const loaded = total != null ? Math.min(rawLoaded, total) : rawLoaded;
const progressBytes = Math.max(0, loaded - bytesNotified);
const progressBytes = loaded - bytesNotified;
const rate = _speedometer(progressBytes);
const inRange = loaded <= total;
bytesNotified = Math.max(bytesNotified, loaded);
bytesNotified = loaded;
const data = {
loaded,
@ -21,7 +21,7 @@ export const progressEventReducer = (listener, isDownloadStream, freq = 3) => {
progress: total ? loaded / total : undefined,
bytes: progressBytes,
rate: rate ? rate : undefined,
estimated: rate && total ? (total - loaded) / rate : undefined,
estimated: rate && total && inRange ? (total - loaded) / rate : undefined,
event: e,
lengthComputable: total != null,
[isDownloadStream ? 'download' : 'upload']: true,

View File

@ -1,7 +1,3 @@
const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']);
const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host);
const DEFAULT_PORTS = {
http: 80,
https: 443,
@ -105,6 +101,6 @@ export default function shouldBypassProxy(location) {
return hostname.endsWith(entryHost);
}
return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost));
return hostname === entryHost;
});
}

View File

@ -1,27 +0,0 @@
import assert from 'assert';
import { progressEventReducer } from '../../../lib/helpers/progressEventReducer.js';
describe('helpers::progressEventReducer', () => {
it('should clamp loaded/progress and avoid negative bytes for out-of-order events', () => {
const events = [];
const [onProgress, flush] = progressEventReducer((data) => {
events.push(data);
}, false, Number.POSITIVE_INFINITY);
onProgress({ lengthComputable: true, loaded: 80, total: 100 });
onProgress({ lengthComputable: true, loaded: 60, total: 100 });
onProgress({ lengthComputable: true, loaded: 180, total: 100 });
flush();
assert.strictEqual(events.length, 3);
assert.strictEqual(events[0].bytes, 80);
assert.strictEqual(events[1].bytes, 0);
const last = events[events.length - 1];
assert.strictEqual(last.loaded, 100);
assert.strictEqual(last.total, 100);
assert.strictEqual(last.progress, 1);
assert.strictEqual(last.upload, true);
assert.strictEqual(last.bytes, 20);
});
});

View File

@ -179,19 +179,15 @@ describe('adapter (vitest browser)', () => {
await responsePromise;
});
it('should sanitize request headers containing CRLF characters', async () => {
const responsePromise = axios('/foo', {
headers: {
'x-test': '\tok\r\nInjected: yes ',
},
});
it('should reject request headers containing CRLF characters', async () => {
await expect(
axios('/foo', {
headers: {
'x-test': 'ok\r\nInjected: yes',
},
})
).rejects.toThrow(/Invalid character in header content/);
const request = await waitForRequest();
expect(request.requestHeaders['x-test']).toBe('okInjected: yes');
expect(request.requestHeaders.Injected).toBeUndefined();
request.respondWith();
await responsePromise;
expect(requests.length).toBe(0);
});
});

View File

@ -26,34 +26,15 @@ const fetchAxios = axios.create({
});
describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () => {
it('should sanitize request headers containing CRLF characters', async () => {
const server = await startHTTPServer(
(req, res) => {
res.setHeader('Content-Type', 'application/json');
res.end(
JSON.stringify({
xTest: req.headers['x-test'],
injected: req.headers.injected ?? null,
})
);
},
{
port: SERVER_PORT,
}
);
try {
const { data } = await fetchAxios.get(`${LOCAL_SERVER_URL}/`, {
it('should reject request headers containing CRLF characters', async () => {
await assert.rejects(
fetchAxios.get(`${LOCAL_SERVER_URL}/`, {
headers: {
'x-test': '\tok\r\nInjected: yes ',
'x-test': 'ok\r\nInjected: yes',
},
});
assert.strictEqual(data.xTest, 'okInjected: yes');
assert.strictEqual(data.injected, null);
} finally {
await stopHTTPServer(server);
}
}),
/(invalid.*header|header.*invalid)/i
);
});
describe('responses', () => {
@ -569,54 +550,6 @@ describe.runIf(typeof fetch === 'function')('supports fetch with nodejs', () =>
await stopHTTPServer(server);
}
});
it('should remove manually set Content-Type without boundary for FormData', async () => {
const form = new FormData();
form.append('foo', 'bar');
const server = await startHTTPServer(
(req, res) => {
const contentType = req.headers['content-type'];
assert.match(contentType, /^multipart\/form-data; boundary=/i);
res.end('OK');
},
{ port: SERVER_PORT }
);
try {
await fetchAxios.post(`http://localhost:${server.address().port}/form`, form, {
headers: { 'Content-Type': 'multipart/form-data' },
});
} finally {
await stopHTTPServer(server);
}
});
it('should preserve Content-Type if it already has boundary', async () => {
const form = new FormData();
form.append('foo', 'bar');
const customBoundary = '----CustomBoundary123';
const server = await startHTTPServer(
(req, res) => {
const contentType = req.headers['content-type'];
assert.ok(contentType.includes(customBoundary));
res.end('OK');
},
{ port: SERVER_PORT }
);
try {
await fetchAxios.post(`http://localhost:${server.address().port}/form`, form, {
headers: {
'Content-Type': `multipart/form-data; boundary=${customBoundary}`,
},
});
} finally {
await stopHTTPServer(server);
}
});
});
describe('env config', () => {

View File

@ -29,7 +29,6 @@ import getStream from 'get-stream';
import bodyParser from 'body-parser';
import { AbortController } from 'abortcontroller-polyfill/dist/cjs-ponyfill.js';
import { lookup } from 'dns';
import { EventEmitter } from 'events';
const OPEN_WEB_PORT = 80;
const SERVER_PORT = 8020;
@ -126,32 +125,15 @@ describe('supports http with nodejs', () => {
}
});
it('should sanitize request headers containing CRLF characters', async () => {
const server = await startHTTPServer(
(req, res) => {
res.setHeader('Content-Type', 'application/json');
res.end(
JSON.stringify({
xTest: req.headers['x-test'],
injected: req.headers.injected ?? null,
})
);
},
{ port: SERVER_PORT }
);
try {
const { data } = await axios.get(`http://localhost:${server.address().port}/`, {
it('should reject request headers containing CRLF characters', async () => {
await assert.rejects(
axios.get('http://localhost:1/', {
headers: {
'x-test': '\tok\r\nInjected: yes ',
'x-test': 'ok\r\nInjected: yes',
},
});
assert.strictEqual(data.xTest, 'okInjected: yes');
assert.strictEqual(data.injected, null);
} finally {
await stopHTTPServer(server);
}
}),
/Invalid character in header content/
);
});
it('should parse the timeout property', async () => {
@ -3835,60 +3817,6 @@ describe('supports http with nodejs', () => {
}
});
it('should reject when only the request socket emits an error', async () => {
const noop = () => {};
const socket = new EventEmitter();
socket.setKeepAlive = noop;
socket.on('error', noop);
const transport = {
request() {
return new (class MockRequest extends EventEmitter {
constructor() {
super();
this.destroyed = false;
}
setTimeout() {}
write() {}
end() {
this.emit('socket', socket);
setImmediate(() => {
socket.emit('error', Object.assign(new Error('write EPIPE'), { code: 'EPIPE' }));
});
}
destroy(err) {
if (this.destroyed) {
return;
}
this.destroyed = true;
err && this.emit('error', err);
this.emit('close');
}
})();
},
};
const error = await Promise.race([
axios.post('http://example.com/', 'test', {
transport,
maxRedirects: 0,
}),
setTimeoutAsync(200).then(() => {
throw new Error('socket error did not reject the request');
}),
]).catch((err) => err);
assert.ok(error instanceof AxiosError);
assert.strictEqual(error.code, 'EPIPE');
assert.strictEqual(error.message, 'write EPIPE');
});
describe('keep-alive', () => {
it('should not fail with "socket hang up" when using timeouts', async () => {
const server = await startHTTPServer(
@ -3910,66 +3838,5 @@ describe('supports http with nodejs', () => {
await stopHTTPServer(server);
}
}, 15000);
it('should remove request socket error listeners after keep-alive requests close', async () => {
const noop = () => {};
const socket = new EventEmitter();
socket.setKeepAlive = noop;
socket.on('error', noop);
const baseErrorListenerCount = socket.listenerCount('error');
const transport = {
request(_, cb) {
return new (class MockRequest extends EventEmitter {
constructor() {
super();
this.destroyed = false;
}
setTimeout() {}
write() {}
end() {
this.emit('socket', socket);
setImmediate(() => {
const response = stream.Readable.from(['ok']);
response.statusCode = 200;
response.headers = {};
cb(response);
this.emit('close');
});
}
destroy(err) {
if (this.destroyed) {
return;
}
this.destroyed = true;
err && this.emit('error', err);
this.emit('close');
}
})();
},
};
await axios.get('http://example.com/first', {
transport,
maxRedirects: 0,
});
await setTimeoutAsync(0);
assert.strictEqual(socket.listenerCount('error'), baseErrorListenerCount);
await axios.get('http://example.com/second', {
transport,
maxRedirects: 0,
});
await setTimeoutAsync(0);
assert.strictEqual(socket.listenerCount('error'), baseErrorListenerCount);
});
});
});

View File

@ -103,20 +103,20 @@ describe('AxiosHeaders', () => {
}
);
it('should sanitize invalid characters in header value', () => {
it('should throw on CRLF in header value', () => {
const headers = new AxiosHeaders();
headers.set('x-test', '\t safe\r\nInjected: true \u0000');
assert.strictEqual(headers.get('x-test'), 'safeInjected: true');
assert.throws(() => {
headers.set('x-test', 'safe\r\nInjected: true');
}, /Invalid character in header content/);
});
it('should sanitize invalid characters in any array header value', () => {
it('should throw on CRLF in any array header value', () => {
const headers = new AxiosHeaders();
headers.set('set-cookie', ['safe=1', ' \tunsafe=1\nInjected: true\r\n ']);
assert.deepStrictEqual(headers.get('set-cookie'), ['safe=1', 'unsafe=1Injected: true']);
assert.throws(() => {
headers.set('set-cookie', ['safe=1', 'unsafe=1\nInjected: true']);
}, /Invalid character in header content/);
});
});

View File

@ -27,32 +27,6 @@ describe('formDataToJSON', () => {
});
});
it('should keep repeatable values flat for 3+ entries', () => {
const formData = new FormData();
formData.append('select3', '301');
formData.append('select3', '302');
formData.append('select3', '303');
expect(formDataToJSON(formData)).toEqual({
select3: ['301', '302', '303'],
});
});
it('should keep nested repeatable values flat for 3+ entries', () => {
const formData = new FormData();
formData.append('foo[bar]', '1');
formData.append('foo[bar]', '2');
formData.append('foo[bar]', '3');
expect(formDataToJSON(formData)).toEqual({
foo: {
bar: ['1', '2', '3'],
},
});
});
it('should convert props with empty brackets to arrays', () => {
const formData = new FormData();

View File

@ -42,30 +42,6 @@ describe('helpers::shouldBypassProxy', () => {
expect(shouldBypassProxy('http://[::1]:8080/')).toBe(true);
});
it('should bypass proxy for 127.0.0.1 when no_proxy contains localhost', () => {
setNoProxy('localhost');
expect(shouldBypassProxy('http://127.0.0.1:7777/')).toBe(true);
});
it('should bypass proxy for [::1] when no_proxy contains localhost', () => {
setNoProxy('localhost');
expect(shouldBypassProxy('http://[::1]:7777/')).toBe(true);
});
it('should bypass proxy for localhost when no_proxy contains 127.0.0.1', () => {
setNoProxy('127.0.0.1');
expect(shouldBypassProxy('http://localhost:7777/')).toBe(true);
});
it('should bypass proxy for localhost when no_proxy contains ::1', () => {
setNoProxy('::1');
expect(shouldBypassProxy('http://localhost:7777/')).toBe(true);
});
it('should match wildcard and explicit ports', () => {
setNoProxy('*.example.com,localhost:8080');