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'); +});