104 lines
3.1 KiB
JavaScript
104 lines
3.1 KiB
JavaScript
import retry from 'retry';
|
|
|
|
const defaultRetryOptions = {
|
|
retries: 5,
|
|
factor: 3,
|
|
minTimeout: 1 * 1000,
|
|
maxTimeout: 60 * 1000,
|
|
randomize: true,
|
|
retryableStatusCodes: [429, 500],
|
|
};
|
|
|
|
let retryOptions = { ...defaultRetryOptions };
|
|
|
|
/**
|
|
* Request hook used to add retry functionality to XHR requests.
|
|
*
|
|
* @param {XMLHttpRequest} request XHR request instance
|
|
* @param {object} metadata Metadata about the request
|
|
* @param {object} metadata.url URL
|
|
* @param {object} metadata.method HTTP method
|
|
* @returns {XMLHttpRequest} request instance optionally modified
|
|
*/
|
|
const xhrRetryRequestHook = (request, metadata) => {
|
|
const { url, method } = metadata;
|
|
|
|
function faultTolerantRequestSend(...args) {
|
|
const operation = retry.operation(retryOptions);
|
|
|
|
operation.attempt(function operationAttempt(currentAttempt) {
|
|
const originalOnReadyStateChange = request.onreadystatechange;
|
|
|
|
/** Overriding/extending XHR function */
|
|
request.onreadystatechange = function onReadyStateChange(...args) {
|
|
originalOnReadyStateChange.apply(request, args);
|
|
|
|
if (retryOptions.retryableStatusCodes.includes(request.status)) {
|
|
const errorMessage = `Attempt to request ${url} failed.`;
|
|
const attemptFailedError = new Error(errorMessage);
|
|
operation.retry(attemptFailedError);
|
|
}
|
|
};
|
|
|
|
/** Call open only on retry (after headers and other things were set in the xhr instance) */
|
|
if (currentAttempt > 1) {
|
|
console.warn(`Requesting ${url}... (attempt: ${currentAttempt})`);
|
|
request.open(method, url, true);
|
|
}
|
|
});
|
|
|
|
originalRequestSend.apply(request, args);
|
|
}
|
|
|
|
/** Overriding/extending XHR function */
|
|
const originalRequestSend = request.send;
|
|
request.send = faultTolerantRequestSend;
|
|
|
|
return request;
|
|
};
|
|
|
|
/**
|
|
* Returns a configured retry request hook function
|
|
* that can be used to add retry functionality to XHR request.
|
|
*
|
|
* Default options:
|
|
* retries: 5
|
|
* factor: 3
|
|
* minTimeout: 1 * 1000
|
|
* maxTimeout: 60 * 1000
|
|
* randomize: true
|
|
*
|
|
* @param {object} options
|
|
* @param {number} options.retires number of retries
|
|
* @param {number} options.factor factor
|
|
* @param {number} options.minTimeout the min timeout
|
|
* @param {number} options.maxTimeout the max timeout
|
|
* @param {boolean} options.randomize randomize
|
|
* @param {array} options.retryableStatusCodes status codes that can trigger retry
|
|
* @returns {function} the configured retry request function
|
|
*/
|
|
export const getXHRRetryRequestHook = (options = {}) => {
|
|
retryOptions = { ...defaultRetryOptions };
|
|
if ('retries' in options) {
|
|
retryOptions.retries = options.retries;
|
|
}
|
|
if ('factor' in options) {
|
|
retryOptions.factor = options.factor;
|
|
}
|
|
if ('minTimeout' in options) {
|
|
retryOptions.minTimeout = options.minTimeout;
|
|
}
|
|
if ('maxTimeout' in options) {
|
|
retryOptions.maxTimeout = options.maxTimeout;
|
|
}
|
|
if ('randomize' in options) {
|
|
retryOptions.randomize = options.randomize;
|
|
}
|
|
if ('retryableStatusCodes' in options) {
|
|
retryOptions.retryableStatusCodes = options.retryableStatusCodes;
|
|
}
|
|
return xhrRetryRequestHook;
|
|
};
|
|
|
|
export default getXHRRetryRequestHook;
|