renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
83 lines • 3.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.wrapWithRetry = wrapWithRetry;
exports.getRetryAfter = getRetryAfter;
const promises_1 = require("timers/promises");
const got_1 = require("got");
const luxon_1 = require("luxon");
const logger_1 = require("../../logger");
const url_1 = require("../url");
const hostDelays = new Map();
const maxRetries = 2;
/**
* Given a task that returns a promise, retry the task if it fails with a
* 429 Too Many Requests or 403 Forbidden response, using the Retry-After
* header to determine the delay.
*
* For response codes other than 429 or 403, or if the Retry-After header
* is not present or invalid, the task is not retried, throwing the error.
*/
async function wrapWithRetry(task, url, getRetryAfter, maxRetryAfter) {
const key = (0, url_1.parseUrl)(url)?.host ?? url;
let retries = 0;
for (;;) {
try {
await hostDelays.get(key);
hostDelays.delete(key);
return await task();
}
catch (err) {
const delaySeconds = getRetryAfter(err);
if (delaySeconds === null) {
throw err;
}
if (retries === maxRetries) {
logger_1.logger.debug(`Retry-After: reached maximum retries (${maxRetries}) for ${url}`);
throw err;
}
if (delaySeconds > maxRetryAfter) {
logger_1.logger.debug(`Retry-After: delay ${delaySeconds} seconds exceeds maxRetryAfter ${maxRetryAfter} seconds for ${url}`);
throw err;
}
logger_1.logger.debug(`Retry-After: will retry ${url} after ${delaySeconds} seconds`);
const delay = Promise.all([
hostDelays.get(key),
(0, promises_1.setTimeout)(1000 * delaySeconds),
]);
hostDelays.set(key, delay);
retries += 1;
}
}
}
function getRetryAfter(err) {
if (!(err instanceof got_1.RequestError)) {
return null;
}
if (!err.response) {
return null;
}
if (err.response.statusCode < 400 || err.response.statusCode >= 500) {
logger_1.logger.debug({ url: err.response.url }, `Retry-After: unexpected status code ${err.response.statusCode}`);
return null;
}
const retryAfter = err.response.headers['retry-after']?.trim();
if (!retryAfter) {
return null;
}
const date = luxon_1.DateTime.fromHTTP(retryAfter);
if (date.isValid) {
const seconds = Math.floor(date.diffNow('seconds').seconds);
if (seconds < 0) {
logger_1.logger.debug({ url: err.response.url, retryAfter }, 'Retry-After: date in the past');
return null;
}
return seconds;
}
const seconds = parseInt(retryAfter);
if (!Number.isNaN(seconds) && seconds >= 0) {
return seconds;
}
logger_1.logger.debug({ url: err.response.url, retryAfter }, 'Retry-After: unsupported format');
return null;
}
//# sourceMappingURL=retry-after.js.map