|
|
import utils from './../utils.js'; import settle from './../core/settle.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 {progressEventReducer} from '../helpers/progressEventReducer.js'; import resolveConfig from "../helpers/resolveConfig.js";
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
export default isXHRAdapterSupported && function (config) { return new Promise(function dispatchXhrRequest(resolve, reject) { const _config = resolveConfig(config); let requestData = _config.data; const requestHeaders = AxiosHeaders.from(_config.headers).normalize(); let {responseType, onUploadProgress, onDownloadProgress} = _config; let onCanceled; let uploadThrottled, downloadThrottled; let flushUpload, flushDownload;
function done() { flushUpload && flushUpload(); // flush events
flushDownload && flushDownload(); // flush events
_config.cancelToken && _config.cancelToken.unsubscribe(onCanceled);
_config.signal && _config.signal.removeEventListener('abort', onCanceled); }
let request = new XMLHttpRequest();
request.open(_config.method.toUpperCase(), _config.url, 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; };
// 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 (onDownloadProgress) { ([downloadThrottled, flushDownload] = progressEventReducer(onDownloadProgress, true)); request.addEventListener('progress', downloadThrottled); }
// Not all browsers support upload events
if (onUploadProgress && request.upload) { ([uploadThrottled, flushUpload] = progressEventReducer(onUploadProgress));
request.upload.addEventListener('progress', uploadThrottled);
request.upload.addEventListener('loadend', flushUpload); }
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(_config.url);
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); }); }
|