Skip to content

Commit 4886b66

Browse files
authored
Retry request even if retry delay is zero (#604)
1 parent e044626 commit 4886b66

File tree

2 files changed

+71
-37
lines changed

2 files changed

+71
-37
lines changed

source/core/Ky.ts

+37-37
Original file line numberDiff line numberDiff line change
@@ -211,33 +211,33 @@ export class Ky {
211211
protected _calculateRetryDelay(error: unknown) {
212212
this._retryCount++;
213213

214-
if (this._retryCount <= this._options.retry.limit && !(error instanceof TimeoutError)) {
215-
if (error instanceof HTTPError) {
216-
if (!this._options.retry.statusCodes.includes(error.response.status)) {
217-
return 0;
218-
}
214+
if (this._retryCount > this._options.retry.limit || error instanceof TimeoutError) {
215+
throw error;
216+
}
219217

220-
const retryAfter = error.response.headers.get('Retry-After');
221-
if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) {
222-
let after = Number(retryAfter) * 1000;
223-
if (Number.isNaN(after)) {
224-
after = Date.parse(retryAfter) - Date.now();
225-
}
218+
if (error instanceof HTTPError) {
219+
if (!this._options.retry.statusCodes.includes(error.response.status)) {
220+
throw error;
221+
}
226222

227-
const max = this._options.retry.maxRetryAfter ?? after;
228-
return after < max ? after : max;
223+
const retryAfter = error.response.headers.get('Retry-After');
224+
if (retryAfter && this._options.retry.afterStatusCodes.includes(error.response.status)) {
225+
let after = Number(retryAfter) * 1000;
226+
if (Number.isNaN(after)) {
227+
after = Date.parse(retryAfter) - Date.now();
229228
}
230229

231-
if (error.response.status === 413) {
232-
return 0;
233-
}
230+
const max = this._options.retry.maxRetryAfter ?? after;
231+
return after < max ? after : max;
234232
}
235233

236-
const retryDelay = this._options.retry.delay(this._retryCount);
237-
return Math.min(this._options.retry.backoffLimit, retryDelay);
234+
if (error.response.status === 413) {
235+
throw error;
236+
}
238237
}
239238

240-
return 0;
239+
const retryDelay = this._options.retry.delay(this._retryCount);
240+
return Math.min(this._options.retry.backoffLimit, retryDelay);
241241
}
242242

243243
protected _decorateResponse(response: Response): Response {
@@ -253,28 +253,28 @@ export class Ky {
253253
return await function_();
254254
} catch (error) {
255255
const ms = Math.min(this._calculateRetryDelay(error), maxSafeTimeout);
256-
if (ms !== 0 && this._retryCount > 0) {
257-
await delay(ms, {signal: this._options.signal});
256+
if (this._retryCount < 1) {
257+
throw error;
258+
}
258259

259-
for (const hook of this._options.hooks.beforeRetry) {
260-
// eslint-disable-next-line no-await-in-loop
261-
const hookResult = await hook({
262-
request: this.request,
263-
options: (this._options as unknown) as NormalizedOptions,
264-
error: error as Error,
265-
retryCount: this._retryCount,
266-
});
267-
268-
// If `stop` is returned from the hook, the retry process is stopped
269-
if (hookResult === stop) {
270-
return;
271-
}
272-
}
260+
await delay(ms, {signal: this._options.signal});
273261

274-
return this._retry(function_);
262+
for (const hook of this._options.hooks.beforeRetry) {
263+
// eslint-disable-next-line no-await-in-loop
264+
const hookResult = await hook({
265+
request: this.request,
266+
options: (this._options as unknown) as NormalizedOptions,
267+
error: error as Error,
268+
retryCount: this._retryCount,
269+
});
270+
271+
// If `stop` is returned from the hook, the retry process is stopped
272+
if (hookResult === stop) {
273+
return;
274+
}
275275
}
276276

277-
throw error;
277+
return this._retry(function_);
278278
}
279279
}
280280

test/retry.ts

+34
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,40 @@ test('not on POST', async t => {
8686
await server.close();
8787
});
8888

89+
test('respect Retry-After: 0 and retry immediately', async t => {
90+
const retryCount = 4;
91+
let requestCount = 0;
92+
93+
const server = await createHttpTestServer();
94+
server.get('/', (_request, response) => {
95+
requestCount++;
96+
97+
if (requestCount === retryCount + 1) {
98+
response.end(fixture);
99+
} else {
100+
response.writeHead(413, {
101+
'Retry-After': 0,
102+
});
103+
104+
response.end('');
105+
}
106+
});
107+
108+
await withPerformance({
109+
t,
110+
expectedDuration: 4 + 4 + 4 + 4,
111+
async test() {
112+
t.is(await ky(server.url, {
113+
retry: retryCount,
114+
}).text(), fixture);
115+
},
116+
});
117+
118+
t.is(requestCount, 5);
119+
120+
await server.close();
121+
});
122+
89123
test('respect 413 Retry-After', async t => {
90124
let requestCount = 0;
91125

0 commit comments

Comments
 (0)