UNPKG

httplease-retry

Version:
68 lines (48 loc) 2.25 kB
'use strict'; const delay = require('./delay'); const assert = require('assert'); function createBackoffRetryFilter({retryTimeoutMillis, shouldRetryFn = defaultShouldRetryFn}) { assert.ok(retryTimeoutMillis >= 0, 'retryTimeoutMillis has to be a non-negative number'); async function retryFilter(requestConfig, next) { const startTimestamp = Date.now(); let attempts = 0; let fallbackDelayMillis = 0; for (;;) { try { return await next(requestConfig); } catch (error) { attempts++; const backoffDelayMillis = getRetryDelayFromHeader(error) || fallbackDelayMillis; fallbackDelayMillis = nextFallbackDelay(fallbackDelayMillis, retryTimeoutMillis); throwIfNotRetryable(error, backoffDelayMillis); await delay(backoffDelayMillis); } } function throwIfNotRetryable(error, backoffDelayMillis) { const nextCallTimestamp = Date.now() + backoffDelayMillis; const timeoutTimestamp = startTimestamp + retryTimeoutMillis; const exceededMaxResponseTime = nextCallTimestamp >= timeoutTimestamp; if (exceededMaxResponseTime || !shouldRetryFn({error, attempts, requestConfig, startTimestamp})) { error.totalFailedAttempts = attempts; error.totalWaitingTime = Date.now() - startTimestamp; throw error; } } } return retryFilter; } function nextFallbackDelay(fallbackDelayMillis, retryTimeoutMillis) { const maxDelayProportionalToMaxWaitingTime = 1/4; const maxDelay = Math.floor(retryTimeoutMillis * maxDelayProportionalToMaxWaitingTime); const newDelay = Math.max(16, fallbackDelayMillis) * 2; return newDelay > maxDelay ? maxDelay : newDelay; } function defaultShouldRetryFn({error}) { const fastRetryStatusCodes = [429, 503]; return error.response && fastRetryStatusCodes.includes(error.response.statusCode); } function getRetryDelayFromHeader(error) { return error.response && parseInt(error.response.headers['retry-after'], 10) * 1000; } module.exports = createBackoffRetryFilter;