Spaces:
Runtime error
Runtime error
; | |
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); | |
}); | |
} | |