From e9436720fcb32b77d39d5477b420b18083e41c02 Mon Sep 17 00:00:00 2001 From: Szymon Marczak <36894700+szmarczak@users.noreply.github.com> Date: Fri, 26 Feb 2021 16:54:41 +0100 Subject: [PATCH] Add read timeout (#1518) --- readme.md | 5 ++++- source/core/utils/timed-out.ts | 24 +++++++++++++++--------- test/timeout.ts | 31 +++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 10 deletions(-) diff --git a/readme.md b/readme.md index 642c2912e..5b7803cfe 100644 --- a/readme.md +++ b/readme.md @@ -518,7 +518,10 @@ This also accepts an `object` with the following fields to constrain the duratio - `socket` starts when the socket is connected. See [request.setTimeout](https://nodejs.org/api/http.html#http_request_settimeout_timeout_callback). - `response` starts when the request has been written to the socket and ends when the response headers are received. - `send` starts when the socket is connected and ends with the request has been written to the socket. -- `request` starts when the request is initiated and ends when the response's end event fires. +- `request` starts when the request is initiated and ends when the response's `end` event fires. +- ~~`read` starts when the `response` event is emitted and ends when the response's `end` event fires.~~ + +**Note:** The `read` timeout is blocked by https://github.com/nodejs/node/issues/35923 ###### retry diff --git a/source/core/utils/timed-out.ts b/source/core/utils/timed-out.ts index ae14ce1ac..8b4ddb167 100644 --- a/source/core/utils/timed-out.ts +++ b/source/core/utils/timed-out.ts @@ -13,11 +13,12 @@ interface TimedOutOptions { export interface Delays { lookup?: number; + socket?: number; connect?: number; secureConnect?: number; - socket?: number; - response?: number; send?: number; + response?: number; + read?: number; request?: number; } @@ -89,14 +90,12 @@ export default (request: ClientRequest, delays: Delays, options: TimedOutOptions } }); - request.once('close', cancelTimeouts); - - once(request, 'response', (response: IncomingMessage): void => { - once(response, 'end', cancelTimeouts); - }); - if (typeof delays.request !== 'undefined') { - addTimeout(delays.request, timeoutHandler, 'request'); + const cancelTimeout = addTimeout(delays.request, timeoutHandler, 'request'); + + once(request, 'response', (response: IncomingMessage): void => { + once(response, 'end', cancelTimeout); + }); } if (typeof delays.socket !== 'undefined') { @@ -168,6 +167,13 @@ export default (request: ClientRequest, delays: Delays, options: TimedOutOptions }); } + if (typeof delays.read !== 'undefined') { + once(request, 'response', (response: IncomingMessage): void => { + const cancelTimeout = addTimeout(delays.read!, timeoutHandler, 'read'); + once(response, 'end', cancelTimeout); + }); + } + return cancelTimeouts; }; diff --git a/test/timeout.ts b/test/timeout.ts index de5b347c2..a49f9f9dd 100644 --- a/test/timeout.ts +++ b/test/timeout.ts @@ -642,6 +642,37 @@ test.serial('no unhandled `Premature close` error', withServer, async (t, server await delay(20); }); +// TODO: use fakeTimers here +test.serial('`read` timeout - promise', withServer, async (t, server, got) => { + server.get('/', (_request, response) => { + response.write('o'); + }); + + await t.throwsAsync(got({ + timeout: { + read: 10 + }, + retry: 0 + }), {message: 'Timeout awaiting \'read\' for 10ms'}); +}); + +// TODO: use fakeTimers here +test.serial.failing('`read` timeout - stream', withServer, async (t, server, got) => { + t.timeout(100); + + server.get('/', (_request, response) => { + response.end('ok'); + }); + + const stream = got.stream({ + timeout: { + read: 10 + } + }); + + await t.throwsAsync(pEvent(stream, 'end'), {message: 'Timeout awaiting \'read\' for 10ms'}); +}); + // TODO: use fakeTimers here test.serial('cancelling the request removes timeouts', withServer, async (t, server, got) => { server.get('/', (_request, response) => {