From 0cd0daff4a972db596d6c495f954dff033ef2faa Mon Sep 17 00:00:00 2001 From: Ram-blip Date: Mon, 6 Jul 2026 14:56:58 +0530 Subject: [PATCH] fix(retry): handle Retry-After zero delay Signed-off-by: Ram-blip --- lib/handler/retry-handler.js | 10 ++++++---- test/retry-handler.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/lib/handler/retry-handler.js b/lib/handler/retry-handler.js index 7928d447ee6..efe43290f98 100644 --- a/lib/handler/retry-handler.js +++ b/lib/handler/retry-handler.js @@ -11,7 +11,7 @@ const { function calculateRetryAfterHeader (retryAfter) { const retryTime = new Date(retryAfter).getTime() - return isNaN(retryTime) ? 0 : retryTime - Date.now() + return isNaN(retryTime) ? null : retryTime - Date.now() } // A stable controller handed to the downstream handler for the lifetime of the @@ -211,9 +211,11 @@ class RetryHandler { } const retryTimeout = - retryAfterHeader > 0 - ? Math.min(retryAfterHeader, maxTimeout) - : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout) + retryAfterHeader === 0 + ? 0 + : retryAfterHeader > 0 + ? Math.min(retryAfterHeader, maxTimeout) + : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout) setTimeout(() => cb(null), retryTimeout) } diff --git a/test/retry-handler.js b/test/retry-handler.js index d222878d1be..c0f5c1f2efb 100644 --- a/test/retry-handler.js +++ b/test/retry-handler.js @@ -5,6 +5,7 @@ const { test, after } = require('node:test') const { createServer } = require('node:http') const { once } = require('node:events') const { Readable } = require('node:stream') +const FakeTimers = require('@sinonjs/fake-timers') const { RetryHandler, Client } = require('..') const { RequestHandler } = require('../lib/api/api-request') @@ -346,6 +347,40 @@ test('Should use retry-after header for retries', async t => { await t.completed }) +test('Should retry immediately when retry-after is zero', async t => { + const clock = FakeTimers.install() + t.after(() => clock.uninstall()) + + let retries = 0 + const handler = new RetryHandler( + { + method: 'GET', + retryOptions: { + maxRetries: 1, + minTimeout: 500 + } + }, + { + dispatch () { + retries++ + }, + handler: {} + } + ) + + handler.onResponseError(null, { + statusCode: 429, + code: 'UND_ERR_REQ_RETRY', + headers: { + 'retry-after': '0' + } + }) + + await clock.tickAsync(0) + + t.assert.strictEqual(retries, 1) +}) + test('Should use retry-after header for retries (date)', async t => { t = tspl(t, { plan: 3 })