From 92b378e5fd95e891e849c7f3fa7483575ab052c3 Mon Sep 17 00:00:00 2001 From: Ivan Motiienko <1161259+katsanva@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:02:00 +0100 Subject: [PATCH] Fix support for `AbortSignal#timeout()` (#2388) --- source/core/index.ts | 7 +++++- test/abort.ts | 52 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/source/core/index.ts b/source/core/index.ts index 842bc4def..12c98d213 100644 --- a/source/core/index.ts +++ b/source/core/index.ts @@ -264,7 +264,12 @@ export default class Request extends Duplex implements RequestEvents { if (this.options.signal) { const abort = () => { - this.destroy(new AbortError(this)); + // See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static#return_value + if (this.options.signal?.reason?.name === 'TimeoutError') { + this.destroy(new TimeoutError(this.options.signal.reason, this.timings!, this)); + } else { + this.destroy(new AbortError(this)); + } }; if (this.options.signal.aborted) { diff --git a/test/abort.ts b/test/abort.ts index e9d151400..960593bd0 100644 --- a/test/abort.ts +++ b/test/abort.ts @@ -324,3 +324,55 @@ test('support setting the signal as a default option', async t => { t.true(signalHandlersRemoved(), 'Abort signal event handlers not removed'); }); + +const timeoutErrorCode = 23; +// See https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static +test('support AbortSignal.timeout()', async t => { + const signal = AbortSignal.timeout(1); + + const p = got('http://example.com', {signal}); + + await t.throwsAsync(p, { + code: timeoutErrorCode, + message: 'The operation was aborted due to timeout', + }); +}); + +test('support AbortSignal.timeout() without user abort', async t => { + const {controller, signalHandlersRemoved} = createAbortController(); + const timeoutSignal = AbortSignal.timeout(1); + const signal = AbortSignal.any([ + controller.signal, + timeoutSignal, + ]); + const p = got('http://example.com', {signal}); + + await t.throwsAsync(p, { + code: timeoutErrorCode, + message: 'The operation was aborted due to timeout', + }); + + t.true(signalHandlersRemoved(), 'Abort signal event handlers not removed'); +}); + +test('support AbortSignal.timeout() with user abort', async t => { + const {controller, signalHandlersRemoved} = createAbortController(); + const timeoutSignal = AbortSignal.timeout(1000); + const signal = AbortSignal.any([ + controller.signal, + timeoutSignal, + ]); + + setTimeout(() => { + controller.abort(); + }, 10); + + const p = got('http://example.com', {signal}); + + await t.throwsAsync(p, { + code: 'ERR_ABORTED', + message: 'This operation was aborted.', + }); + + t.true(signalHandlersRemoved(), 'Abort signal event handlers not removed'); +});