@aws-amplify/core
Version:
Core category of aws-amplify
96 lines (94 loc) • 3.7 kB
JavaScript
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
Object.defineProperty(exports, "__esModule", { value: true });
exports.retryMiddlewareFactory = void 0;
const DEFAULT_RETRY_ATTEMPTS = 3;
/**
* Retry middleware
*/
const retryMiddlewareFactory = ({ maxAttempts = DEFAULT_RETRY_ATTEMPTS, retryDecider, computeDelay, abortSignal, }) => {
if (maxAttempts < 1) {
throw new Error('maxAttempts must be greater than 0');
}
return (next, context) => async function retryMiddleware(request) {
let error;
let attemptsCount = context.attemptsCount ?? 0;
let response;
// When retry is not needed or max attempts is reached, either error or response will be set. This function handles either cases.
const handleTerminalErrorOrResponse = () => {
if (response) {
addOrIncrementMetadataAttempts(response, attemptsCount);
return response;
}
else {
addOrIncrementMetadataAttempts(error, attemptsCount);
throw error;
}
};
while (!abortSignal?.aborted && attemptsCount < maxAttempts) {
try {
response = await next(request);
error = undefined;
}
catch (e) {
error = e;
response = undefined;
}
// context.attemptsCount may be updated after calling next handler which may retry the request by itself.
attemptsCount =
(context.attemptsCount ?? 0) > attemptsCount
? (context.attemptsCount ?? 0)
: attemptsCount + 1;
context.attemptsCount = attemptsCount;
const { isCredentialsExpiredError, retryable } = await retryDecider(response, error, context);
if (retryable) {
// Setting isCredentialsInvalid flag to notify signing middleware to forceRefresh credentials provider.
context.isCredentialsExpired = !!isCredentialsExpiredError;
if (!abortSignal?.aborted && attemptsCount < maxAttempts) {
// prevent sleep for last attempt or cancelled request;
const delay = computeDelay(attemptsCount);
await cancellableSleep(delay, abortSignal);
}
continue;
}
else {
return handleTerminalErrorOrResponse();
}
}
if (abortSignal?.aborted) {
throw new Error('Request aborted.');
}
else {
return handleTerminalErrorOrResponse();
}
};
};
exports.retryMiddlewareFactory = retryMiddlewareFactory;
const cancellableSleep = (timeoutMs, abortSignal) => {
if (abortSignal?.aborted) {
return Promise.resolve();
}
let timeoutId;
let sleepPromiseResolveFn;
const sleepPromise = new Promise(resolve => {
sleepPromiseResolveFn = resolve;
timeoutId = setTimeout(resolve, timeoutMs);
});
abortSignal?.addEventListener('abort', function cancelSleep(_) {
clearTimeout(timeoutId);
abortSignal?.removeEventListener('abort', cancelSleep);
sleepPromiseResolveFn();
});
return sleepPromise;
};
const addOrIncrementMetadataAttempts = (nextHandlerOutput, attempts) => {
if (Object.prototype.toString.call(nextHandlerOutput) !== '[object Object]') {
return;
}
nextHandlerOutput.$metadata = {
...(nextHandlerOutput.$metadata ?? {}),
attempts,
};
};
//# sourceMappingURL=middleware.js.map
;