123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- 'use strict';
- import utils from './../utils.js';
- import settle from './../core/settle.js';
- import cookies from './../helpers/cookies.js';
- import buildURL from './../helpers/buildURL.js';
- import buildFullPath from '../core/buildFullPath.js';
- import isURLSameOrigin from './../helpers/isURLSameOrigin.js';
- import transitionalDefaults from '../defaults/transitional.js';
- import AxiosError from '../core/AxiosError.js';
- import CanceledError from '../cancel/CanceledError.js';
- import parseProtocol from '../helpers/parseProtocol.js';
- import platform from '../platform/index.js';
- import AxiosHeaders from '../core/AxiosHeaders.js';
- import speedometer from '../helpers/speedometer.js';
- function progressEventReducer(listener, isDownloadStream) {
- let bytesNotified = 0;
- const _speedometer = speedometer(50, 250);
- return e => {
- const loaded = e.loaded;
- const total = e.lengthComputable ? e.total : undefined;
- const progressBytes = loaded - bytesNotified;
- const rate = _speedometer(progressBytes);
- const inRange = loaded <= total;
- bytesNotified = loaded;
- const data = {
- loaded,
- total,
- progress: total ? (loaded / total) : undefined,
- bytes: progressBytes,
- rate: rate ? rate : undefined,
- estimated: rate && total && inRange ? (total - loaded) / rate : undefined,
- event: e
- };
- data[isDownloadStream ? 'download' : 'upload'] = true;
- listener(data);
- };
- }
- const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
- export default isXHRAdapterSupported && function (config) {
- return new Promise(function dispatchXhrRequest(resolve, reject) {
- let requestData = config.data;
- const requestHeaders = AxiosHeaders.from(config.headers).normalize();
- let {responseType, withXSRFToken} = config;
- let onCanceled;
- function done() {
- if (config.cancelToken) {
- config.cancelToken.unsubscribe(onCanceled);
- }
- if (config.signal) {
- config.signal.removeEventListener('abort', onCanceled);
- }
- }
- let contentType;
- if (utils.isFormData(requestData)) {
- if (platform.hasStandardBrowserEnv || platform.hasStandardBrowserWebWorkerEnv) {
- requestHeaders.setContentType(false); // Let the browser set it
- } else if ((contentType = requestHeaders.getContentType()) !== false) {
- // fix semicolon duplication issue for ReactNative FormData implementation
- const [type, ...tokens] = contentType ? contentType.split(';').map(token => token.trim()).filter(Boolean) : [];
- requestHeaders.setContentType([type || 'multipart/form-data', ...tokens].join('; '));
- }
- }
- let request = new XMLHttpRequest();
- // HTTP basic authentication
- if (config.auth) {
- const username = config.auth.username || '';
- const password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : '';
- requestHeaders.set('Authorization', 'Basic ' + btoa(username + ':' + password));
- }
- const fullPath = buildFullPath(config.baseURL, config.url);
- request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
- // Set the request timeout in MS
- request.timeout = config.timeout;
- function onloadend() {
- if (!request) {
- return;
- }
- // Prepare the response
- const responseHeaders = AxiosHeaders.from(
- 'getAllResponseHeaders' in request && request.getAllResponseHeaders()
- );
- const responseData = !responseType || responseType === 'text' || responseType === 'json' ?
- request.responseText : request.response;
- const response = {
- data: responseData,
- status: request.status,
- statusText: request.statusText,
- headers: responseHeaders,
- config,
- request
- };
- settle(function _resolve(value) {
- resolve(value);
- done();
- }, function _reject(err) {
- reject(err);
- done();
- }, response);
- // Clean up request
- request = null;
- }
- if ('onloadend' in request) {
- // Use onloadend if available
- request.onloadend = onloadend;
- } else {
- // Listen for ready state to emulate onloadend
- request.onreadystatechange = function handleLoad() {
- if (!request || request.readyState !== 4) {
- return;
- }
- // The request errored out and we didn't get a response, this will be
- // handled by onerror instead
- // With one exception: request that using file: protocol, most browsers
- // will return status as 0 even though it's a successful request
- if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
- return;
- }
- // readystate handler is calling before onerror or ontimeout handlers,
- // so we should call onloadend on the next 'tick'
- setTimeout(onloadend);
- };
- }
- // Handle browser request cancellation (as opposed to a manual cancellation)
- request.onabort = function handleAbort() {
- if (!request) {
- return;
- }
- reject(new AxiosError('Request aborted', AxiosError.ECONNABORTED, config, request));
- // Clean up request
- request = null;
- };
- // Handle low level network errors
- request.onerror = function handleError() {
- // Real errors are hidden from us by the browser
- // onerror should only fire if it's a network error
- reject(new AxiosError('Network Error', AxiosError.ERR_NETWORK, config, request));
- // Clean up request
- request = null;
- };
- // Handle timeout
- request.ontimeout = function handleTimeout() {
- let timeoutErrorMessage = config.timeout ? 'timeout of ' + config.timeout + 'ms exceeded' : 'timeout exceeded';
- const transitional = config.transitional || transitionalDefaults;
- if (config.timeoutErrorMessage) {
- timeoutErrorMessage = config.timeoutErrorMessage;
- }
- reject(new AxiosError(
- timeoutErrorMessage,
- transitional.clarifyTimeoutError ? AxiosError.ETIMEDOUT : AxiosError.ECONNABORTED,
- config,
- request));
- // Clean up request
- request = null;
- };
- // Add xsrf header
- // This is only done if running in a standard browser environment.
- // Specifically not if we're in a web worker, or react-native.
- if(platform.hasStandardBrowserEnv) {
- withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(config));
- if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(fullPath))) {
- // Add xsrf header
- const xsrfValue = config.xsrfHeaderName && config.xsrfCookieName && cookies.read(config.xsrfCookieName);
- if (xsrfValue) {
- requestHeaders.set(config.xsrfHeaderName, xsrfValue);
- }
- }
- }
- // Remove Content-Type if data is undefined
- requestData === undefined && requestHeaders.setContentType(null);
- // Add headers to the request
- if ('setRequestHeader' in request) {
- utils.forEach(requestHeaders.toJSON(), function setRequestHeader(val, key) {
- request.setRequestHeader(key, val);
- });
- }
- // Add withCredentials to request if needed
- if (!utils.isUndefined(config.withCredentials)) {
- request.withCredentials = !!config.withCredentials;
- }
- // Add responseType to request if needed
- if (responseType && responseType !== 'json') {
- request.responseType = config.responseType;
- }
- // Handle progress if needed
- if (typeof config.onDownloadProgress === 'function') {
- request.addEventListener('progress', progressEventReducer(config.onDownloadProgress, true));
- }
- // Not all browsers support upload events
- if (typeof config.onUploadProgress === 'function' && request.upload) {
- request.upload.addEventListener('progress', progressEventReducer(config.onUploadProgress));
- }
- if (config.cancelToken || config.signal) {
- // Handle cancellation
- // eslint-disable-next-line func-names
- onCanceled = cancel => {
- if (!request) {
- return;
- }
- reject(!cancel || cancel.type ? new CanceledError(null, config, request) : cancel);
- request.abort();
- request = null;
- };
- config.cancelToken && config.cancelToken.subscribe(onCanceled);
- if (config.signal) {
- config.signal.aborted ? onCanceled() : config.signal.addEventListener('abort', onCanceled);
- }
- }
- const protocol = parseProtocol(fullPath);
- if (protocol && platform.protocols.indexOf(protocol) === -1) {
- reject(new AxiosError('Unsupported protocol ' + protocol + ':', AxiosError.ERR_BAD_REQUEST, config));
- return;
- }
- // Send the request
- request.send(requestData || null);
- });
- }
|