123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- import platform from "../platform/index.js";
- import utils from "../utils.js";
- import AxiosError from "../core/AxiosError.js";
- import composeSignals from "../helpers/composeSignals.js";
- import {trackStream} from "../helpers/trackStream.js";
- import AxiosHeaders from "../core/AxiosHeaders.js";
- import {progressEventReducer, progressEventDecorator, asyncDecorator} from "../helpers/progressEventReducer.js";
- import resolveConfig from "../helpers/resolveConfig.js";
- import settle from "../core/settle.js";
- const DEFAULT_CHUNK_SIZE = 64 * 1024;
- const {isFunction} = utils;
- const globalFetchAPI = (({Request, Response}) => ({
- Request, Response
- }))(utils.global);
- const {
- ReadableStream, TextEncoder
- } = utils.global;
- const test = (fn, ...args) => {
- try {
- return !!fn(...args);
- } catch (e) {
- return false
- }
- }
- const factory = (env) => {
- env = utils.merge.call({
- skipUndefined: true
- }, globalFetchAPI, env);
- const {fetch: envFetch, Request, Response} = env;
- const isFetchSupported = envFetch ? isFunction(envFetch) : typeof fetch === 'function';
- const isRequestSupported = isFunction(Request);
- const isResponseSupported = isFunction(Response);
- if (!isFetchSupported) {
- return false;
- }
- const isReadableStreamSupported = isFetchSupported && isFunction(ReadableStream);
- const encodeText = isFetchSupported && (typeof TextEncoder === 'function' ?
- ((encoder) => (str) => encoder.encode(str))(new TextEncoder()) :
- async (str) => new Uint8Array(await new Request(str).arrayBuffer())
- );
- const supportsRequestStream = isRequestSupported && isReadableStreamSupported && test(() => {
- let duplexAccessed = false;
- const hasContentType = new Request(platform.origin, {
- body: new ReadableStream(),
- method: 'POST',
- get duplex() {
- duplexAccessed = true;
- return 'half';
- },
- }).headers.has('Content-Type');
- return duplexAccessed && !hasContentType;
- });
- const supportsResponseStream = isResponseSupported && isReadableStreamSupported &&
- test(() => utils.isReadableStream(new Response('').body));
- const resolvers = {
- stream: supportsResponseStream && ((res) => res.body)
- };
- isFetchSupported && ((() => {
- ['text', 'arrayBuffer', 'blob', 'formData', 'stream'].forEach(type => {
- !resolvers[type] && (resolvers[type] = (res, config) => {
- let method = res && res[type];
- if (method) {
- return method.call(res);
- }
- throw new AxiosError(`Response type '${type}' is not supported`, AxiosError.ERR_NOT_SUPPORT, config);
- })
- });
- })());
- const getBodyLength = async (body) => {
- if (body == null) {
- return 0;
- }
- if (utils.isBlob(body)) {
- return body.size;
- }
- if (utils.isSpecCompliantForm(body)) {
- const _request = new Request(platform.origin, {
- method: 'POST',
- body,
- });
- return (await _request.arrayBuffer()).byteLength;
- }
- if (utils.isArrayBufferView(body) || utils.isArrayBuffer(body)) {
- return body.byteLength;
- }
- if (utils.isURLSearchParams(body)) {
- body = body + '';
- }
- if (utils.isString(body)) {
- return (await encodeText(body)).byteLength;
- }
- }
- const resolveBodyLength = async (headers, body) => {
- const length = utils.toFiniteNumber(headers.getContentLength());
- return length == null ? getBodyLength(body) : length;
- }
- return async (config) => {
- let {
- url,
- method,
- data,
- signal,
- cancelToken,
- timeout,
- onDownloadProgress,
- onUploadProgress,
- responseType,
- headers,
- withCredentials = 'same-origin',
- fetchOptions
- } = resolveConfig(config);
- let _fetch = envFetch || fetch;
- responseType = responseType ? (responseType + '').toLowerCase() : 'text';
- let composedSignal = composeSignals([signal, cancelToken && cancelToken.toAbortSignal()], timeout);
- let request = null;
- const unsubscribe = composedSignal && composedSignal.unsubscribe && (() => {
- composedSignal.unsubscribe();
- });
- let requestContentLength;
- try {
- if (
- onUploadProgress && supportsRequestStream && method !== 'get' && method !== 'head' &&
- (requestContentLength = await resolveBodyLength(headers, data)) !== 0
- ) {
- let _request = new Request(url, {
- method: 'POST',
- body: data,
- duplex: "half"
- });
- let contentTypeHeader;
- if (utils.isFormData(data) && (contentTypeHeader = _request.headers.get('content-type'))) {
- headers.setContentType(contentTypeHeader)
- }
- if (_request.body) {
- const [onProgress, flush] = progressEventDecorator(
- requestContentLength,
- progressEventReducer(asyncDecorator(onUploadProgress))
- );
- data = trackStream(_request.body, DEFAULT_CHUNK_SIZE, onProgress, flush);
- }
- }
- if (!utils.isString(withCredentials)) {
- withCredentials = withCredentials ? 'include' : 'omit';
- }
- // Cloudflare Workers throws when credentials are defined
- // see https://github.com/cloudflare/workerd/issues/902
- const isCredentialsSupported = isRequestSupported && "credentials" in Request.prototype;
- const resolvedOptions = {
- ...fetchOptions,
- signal: composedSignal,
- method: method.toUpperCase(),
- headers: headers.normalize().toJSON(),
- body: data,
- duplex: "half",
- credentials: isCredentialsSupported ? withCredentials : undefined
- };
- request = isRequestSupported && new Request(url, resolvedOptions);
- let response = await (isRequestSupported ? _fetch(request, fetchOptions) : _fetch(url, resolvedOptions));
- const isStreamResponse = supportsResponseStream && (responseType === 'stream' || responseType === 'response');
- if (supportsResponseStream && (onDownloadProgress || (isStreamResponse && unsubscribe))) {
- const options = {};
- ['status', 'statusText', 'headers'].forEach(prop => {
- options[prop] = response[prop];
- });
- const responseContentLength = utils.toFiniteNumber(response.headers.get('content-length'));
- const [onProgress, flush] = onDownloadProgress && progressEventDecorator(
- responseContentLength,
- progressEventReducer(asyncDecorator(onDownloadProgress), true)
- ) || [];
- response = new Response(
- trackStream(response.body, DEFAULT_CHUNK_SIZE, onProgress, () => {
- flush && flush();
- unsubscribe && unsubscribe();
- }),
- options
- );
- }
- responseType = responseType || 'text';
- let responseData = await resolvers[utils.findKey(resolvers, responseType) || 'text'](response, config);
- !isStreamResponse && unsubscribe && unsubscribe();
- return await new Promise((resolve, reject) => {
- settle(resolve, reject, {
- data: responseData,
- headers: AxiosHeaders.from(response.headers),
- status: response.status,
- statusText: response.statusText,
- config,
- request
- })
- })
- } catch (err) {
- unsubscribe && unsubscribe();
- if (err && err.name === 'TypeError' && /Load failed|fetch/i.test(err.message)) {
- throw Object.assign(
- new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request),
- {
- cause: err.cause || err
- }
- )
- }
- throw AxiosError.from(err, err && err.code, config, request);
- }
- }
- }
- const seedCache = new Map();
- export const getFetch = (config) => {
- let env = config ? config.env : {};
- const {fetch, Request, Response} = env;
- const seeds = [
- Request, Response, fetch
- ];
- let len = seeds.length, i = len,
- seed, target, map = seedCache;
- while (i--) {
- seed = seeds[i];
- target = map.get(seed);
- target === undefined && map.set(seed, target = (i ? new Map() : factory(env)))
- map = target;
- }
- return target;
- };
- const adapter = getFetch();
- export default adapter;
|