@azure/core-rest-pipeline
Version:
Isomorphic client library for making HTTP requests in node.js and browser.
105 lines • 5.42 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { delay } from "../util/helpers.js";
import { createClientLogger } from "@azure/logger";
import { AbortError } from "@azure/abort-controller";
import { DEFAULT_RETRY_POLICY_COUNT } from "../constants.js";
const retryPolicyLogger = createClientLogger("core-rest-pipeline retryPolicy");
/**
* The programmatic identifier of the retryPolicy.
*/
const retryPolicyName = "retryPolicy";
/**
* retryPolicy is a generic policy to enable retrying requests when certain conditions are met
*/
export function retryPolicy(strategies, options = { maxRetries: DEFAULT_RETRY_POLICY_COUNT }) {
const logger = options.logger || retryPolicyLogger;
return {
name: retryPolicyName,
async sendRequest(request, next) {
var _a, _b;
let response;
let responseError;
let retryCount = -1;
retryRequest: while (true) {
retryCount += 1;
response = undefined;
responseError = undefined;
try {
logger.info(`Retry ${retryCount}: Attempting to send request`, request.requestId);
response = await next(request);
logger.info(`Retry ${retryCount}: Received a response from request`, request.requestId);
}
catch (e) {
logger.error(`Retry ${retryCount}: Received an error from request`, request.requestId);
// RestErrors are valid targets for the retry strategies.
// If none of the retry strategies can work with them, they will be thrown later in this policy.
// If the received error is not a RestError, it is immediately thrown.
responseError = e;
if (!e || responseError.name !== "RestError") {
throw e;
}
response = responseError.response;
}
if ((_a = request.abortSignal) === null || _a === void 0 ? void 0 : _a.aborted) {
logger.error(`Retry ${retryCount}: Request aborted.`);
const abortError = new AbortError();
throw abortError;
}
if (retryCount >= ((_b = options.maxRetries) !== null && _b !== void 0 ? _b : DEFAULT_RETRY_POLICY_COUNT)) {
logger.info(`Retry ${retryCount}: Maximum retries reached. Returning the last received response, or throwing the last received error.`);
if (responseError) {
throw responseError;
}
else if (response) {
return response;
}
else {
throw new Error("Maximum retries reached with no response or error to throw");
}
}
logger.info(`Retry ${retryCount}: Processing ${strategies.length} retry strategies.`);
strategiesLoop: for (const strategy of strategies) {
const strategyLogger = strategy.logger || retryPolicyLogger;
strategyLogger.info(`Retry ${retryCount}: Processing retry strategy ${strategy.name}.`);
const modifiers = strategy.retry({
retryCount,
response,
responseError,
});
if (modifiers.skipStrategy) {
strategyLogger.info(`Retry ${retryCount}: Skipped.`);
continue strategiesLoop;
}
const { errorToThrow, retryAfterInMs, redirectTo } = modifiers;
if (errorToThrow) {
strategyLogger.error(`Retry ${retryCount}: Retry strategy ${strategy.name} throws error:`, errorToThrow);
throw errorToThrow;
}
if (retryAfterInMs || retryAfterInMs === 0) {
strategyLogger.info(`Retry ${retryCount}: Retry strategy ${strategy.name} retries after ${retryAfterInMs}`);
await delay(retryAfterInMs, undefined, { abortSignal: request.abortSignal });
continue retryRequest;
}
if (redirectTo) {
strategyLogger.info(`Retry ${retryCount}: Retry strategy ${strategy.name} redirects to ${redirectTo}`);
request.url = redirectTo;
continue retryRequest;
}
}
if (responseError) {
logger.info(`None of the retry strategies could work with the received error. Throwing it.`);
throw responseError;
}
if (response) {
logger.info(`None of the retry strategies could work with the received response. Returning it.`);
return response;
}
// If all the retries skip and there's no response,
// we're still in the retry loop, so a new request will be sent
// until `maxRetries` is reached.
}
},
};
}
//# sourceMappingURL=retryPolicy.js.map