@gentrace/core
Version:
Core Gentrace Node.JS library
319 lines (277 loc) • 9.46 kB
JavaScript
;
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);
});
};