UNPKG

@gentrace/core

Version:
319 lines (277 loc) 9.46 kB
"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(); const responseType = config.responseType; let onCanceled; function done() { if (config.cancelToken) { config.cancelToken.unsubscribe(onCanceled); } if (config.signal) { config.signal.removeEventListener("abort", onCanceled); } } if (utils.isFormData(requestData)) { if ( platform.isStandardBrowserEnv || platform.isStandardBrowserWebWorkerEnv ) { requestHeaders.setContentType(false); // Let the browser set it } else { requestHeaders.setContentType("multipart/form-data;", false); // mobile/desktop app frameworks } } 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.isStandardBrowserEnv) { // Add xsrf header const xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && 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); }); };