From ca21e25f68698fdc6d2ed57d7cdf3cb54498bd82 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 25 Sep 2023 14:22:36 -0400 Subject: [PATCH] feat(client): handle retry-after with a date (#242) --- src/core.ts | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/core.ts b/src/core.ts index 0337797a..6f0e9a1c 100644 --- a/src/core.ts +++ b/src/core.ts @@ -502,32 +502,37 @@ export abstract class APIClient { retriesRemaining -= 1; // About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After - // - // TODO: we may want to handle the case where the header is using the http-date syntax: "Retry-After: ". - // See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax for details. - const retryAfter = parseInt(responseHeaders?.['retry-after'] || ''); + let timeoutMillis: number | undefined; + const retryAfterHeader = responseHeaders?.['retry-after']; + if (retryAfterHeader) { + const timeoutSeconds = parseInt(retryAfterHeader); + if (!Number.isNaN(timeoutSeconds)) { + timeoutMillis = timeoutSeconds * 1000; + } else { + timeoutMillis = Date.parse(retryAfterHeader) - Date.now(); + } + } - const maxRetries = options.maxRetries ?? this.maxRetries; - const timeout = this.calculateRetryTimeoutSeconds(retriesRemaining, retryAfter, maxRetries) * 1000; - await sleep(timeout); + // If the API asks us to wait a certain amount of time (and it's a reasonable amount), + // just do what it says, but otherwise calculate a default + if ( + !timeoutMillis || + !Number.isInteger(timeoutMillis) || + timeoutMillis <= 0 || + timeoutMillis > 60 * 1000 + ) { + const maxRetries = options.maxRetries ?? this.maxRetries; + timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries); + } + await sleep(timeoutMillis); return this.makeRequest(options, retriesRemaining); } - private calculateRetryTimeoutSeconds( - retriesRemaining: number, - retryAfter: number, - maxRetries: number, - ): number { + private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number { const initialRetryDelay = 0.5; const maxRetryDelay = 2; - // If the API asks us to wait a certain amount of time (and it's a reasonable amount), - // just do what it says. - if (Number.isInteger(retryAfter) && retryAfter <= 60) { - return retryAfter; - } - const numRetries = maxRetries - retriesRemaining; // Apply exponential backoff, but not more than the max. @@ -536,7 +541,7 @@ export abstract class APIClient { // Apply some jitter, plus-or-minus half a second. const jitter = Math.random() - 0.5; - return sleepSeconds + jitter; + return (sleepSeconds + jitter) * 1000; } private getUserAgent(): string {