From d047e9907e9f6ca45714f5a02471200cce111143 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:43:54 +0200 Subject: [PATCH] fix: use fasttimers for all connection timeouts (#3552) (#3675) * fix: use fasttimers for all connection timeouts * Apply suggestions from code review * activate some tests * also use fastTimers in connect.js * fix tests * fix tests * fix tests * fix: use native timeouts for TIMEOUT_IDLE, rename TIMEOUT_IDLE to TIMEOUT_KEEP_ALIVE (#3554) * fix: use native timeouts for TIMEOUT_IDLE, rename TIMEOUT_IDLE to TIMEOUT_KEEP_ALIVE * ensure that fast timers and native timers are set properly * . * . * rename import * more informative connection error * ignore request-timeout binary file, rename clearAll to reset * fix * add test * use queueMicrotask earlier in the socket callbacks (cherry picked from commit dca0aa0998cbdef28916b23d6300beb2fd979140) Co-authored-by: Aras Abbasi --- .gitignore | 3 + .npmignore | 3 + lib/core/connect.js | 69 +++++++++++----- lib/dispatcher/client-h1.js | 49 ++++++++---- lib/util/timers.js | 49 +++++++++++- test/connect-timeout.js | 11 ++- test/issue-3356.js | 5 +- test/request-timeout.js | 155 ++++++++++-------------------------- 8 files changed, 191 insertions(+), 153 deletions(-) diff --git a/.gitignore b/.gitignore index 60aa663c838..7cba7df889f 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,6 @@ undici-fetch.js .npmrc .tap + +# File generated by /test/request-timeout.js +test/request-timeout.10mb.bin diff --git a/.npmignore b/.npmignore index 879c6669f03..461d334ef36 100644 --- a/.npmignore +++ b/.npmignore @@ -11,3 +11,6 @@ lib/llhttp/llhttp.wasm !index.d.ts !docs/docs/**/* !scripts/strip-comments.js + +# File generated by /test/request-timeout.js +test/request-timeout.10mb.bin diff --git a/lib/core/connect.js b/lib/core/connect.js index 50578534ae8..8ab21fcd5fc 100644 --- a/lib/core/connect.js +++ b/lib/core/connect.js @@ -6,6 +6,8 @@ const util = require('./util') const { InvalidArgumentError, ConnectTimeoutError } = require('./errors') const timers = require('../util/timers') +function noop () {} + let tls // include tls conditionally since it is not always available // TODO: session re-use does not wait for the first @@ -96,6 +98,8 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess const session = customSession || sessionCache.get(sessionKey) || null + port = port || 443 + socket = tls.connect({ highWaterMark: 16384, // TLS in node can't have bigger HWM anyway... ...options, @@ -105,7 +109,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess // TODO(HTTP/2): Add support for h2c ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'], socket: httpSocket, // upgrade socket connection - port: port || 443, + port, host: hostname }) @@ -116,11 +120,14 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess }) } else { assert(!httpSocket, 'httpSocket can only be sent on TLS update') + + port = port || 80 + socket = net.connect({ highWaterMark: 64 * 1024, // Same as nodejs fs streams. ...options, localAddress, - port: port || 80, + port, host: hostname }) } @@ -131,12 +138,12 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess socket.setKeepAlive(true, keepAliveInitialDelay) } - const cancelConnectTimeout = setupConnectTimeout(new WeakRef(socket), timeout) + const clearConnectTimeout = setupConnectTimeout(new WeakRef(socket), { timeout, hostname, port }) socket .setNoDelay(true) .once(protocol === 'https:' ? 'secureConnect' : 'connect', function () { - cancelConnectTimeout() + queueMicrotask(clearConnectTimeout) if (callback) { const cb = callback @@ -145,7 +152,7 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess } }) .on('error', function (err) { - cancelConnectTimeout() + queueMicrotask(clearConnectTimeout) if (callback) { const cb = callback @@ -158,50 +165,70 @@ function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, sess } } +/** + * @param {WeakRef} socketWeakRef + * @param {object} opts + * @param {number} opts.timeout + * @param {string} opts.hostname + * @param {number} opts.port + * @returns {() => void} + */ const setupConnectTimeout = process.platform === 'win32' - ? (socket, timeout) => { - if (!timeout) { - return () => { } + ? (socketWeakRef, opts) => { + if (!opts.timeout) { + return noop } let s1 = null let s2 = null - const timer = timers.setTimeout(() => { + const fastTimer = timers.setFastTimeout(() => { // setImmediate is added to make sure that we prioritize socket error events over timeouts s1 = setImmediate(() => { // Windows needs an extra setImmediate probably due to implementation differences in the socket logic - s2 = setImmediate(() => onConnectTimeout(socket.deref())) + s2 = setImmediate(() => onConnectTimeout(socketWeakRef.deref(), opts)) }) - }, timeout) + }, opts.timeout) return () => { - timers.clearTimeout(timer) + timers.clearFastTimeout(fastTimer) clearImmediate(s1) clearImmediate(s2) } } - : (socket, timeout) => { - if (!timeout) { - return () => { } + : (socketWeakRef, opts) => { + if (!opts.timeout) { + return noop } let s1 = null - const timer = timers.setTimeout(() => { + const fastTimer = timers.setFastTimeout(() => { // setImmediate is added to make sure that we prioritize socket error events over timeouts s1 = setImmediate(() => { - onConnectTimeout(socket.deref()) + onConnectTimeout(socketWeakRef.deref(), opts) }) - }, timeout) + }, opts.timeout) return () => { - timers.clearTimeout(timer) + timers.clearFastTimeout(fastTimer) clearImmediate(s1) } } -function onConnectTimeout (socket) { +/** + * @param {net.Socket} socket + * @param {object} opts + * @param {number} opts.timeout + * @param {string} opts.hostname + * @param {number} opts.port + */ +function onConnectTimeout (socket, opts) { let message = 'Connect Timeout Error' if (Array.isArray(socket.autoSelectFamilyAttemptedAddresses)) { - message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')})` + message += ` (attempted addresses: ${socket.autoSelectFamilyAttemptedAddresses.join(', ')},` + } else { + message += ` (attempted address: ${opts.hostname}:${opts.port},` } + + message += ` timeout: ${opts.timeout}ms)` + util.destroy(socket, new ConnectTimeoutError(message)) } diff --git a/lib/dispatcher/client-h1.js b/lib/dispatcher/client-h1.js index 4a26f641283..40628e53d4c 100644 --- a/lib/dispatcher/client-h1.js +++ b/lib/dispatcher/client-h1.js @@ -131,9 +131,17 @@ let currentBufferRef = null let currentBufferSize = 0 let currentBufferPtr = null -const TIMEOUT_HEADERS = 1 -const TIMEOUT_BODY = 2 -const TIMEOUT_IDLE = 3 +const USE_NATIVE_TIMER = 0 +const USE_FAST_TIMER = 1 + +// Use fast timers for headers and body to take eventual event loop +// latency into account. +const TIMEOUT_HEADERS = 2 | USE_FAST_TIMER +const TIMEOUT_BODY = 4 | USE_FAST_TIMER + +// Use native timers to ignore event loop latency for keep-alive +// handling. +const TIMEOUT_KEEP_ALIVE = 8 | USE_NATIVE_TIMER class Parser { constructor (client, socket, { exports }) { @@ -165,18 +173,29 @@ class Parser { } setTimeout (delay, type) { - this.timeoutType = type - if (delay !== this.timeoutValue) { - this.timeout && timers.clearTimeout(this.timeout) + // If the existing timer and the new timer are of different timer type + // (fast or native) or have different delay, we need to clear the existing + // timer and set a new one. + if ( + delay !== this.timeoutValue || + (type & USE_FAST_TIMER) ^ (this.timeoutType & USE_FAST_TIMER) + ) { + // If a timeout is already set, clear it with clearTimeout of the fast + // timer implementation, as it can clear fast and native timers. + if (this.timeout) { + timers.clearTimeout(this.timeout) + this.timeout = null + } + if (delay) { - this.timeout = timers.setTimeout(onParserTimeout, delay, new WeakRef(this)) - // istanbul ignore else: only for jest - if (this.timeout.unref) { + if (type & USE_FAST_TIMER) { + this.timeout = timers.setFastTimeout(onParserTimeout, delay, new WeakRef(this)) + } else { + this.timeout = setTimeout(onParserTimeout, delay, new WeakRef(this)) this.timeout.unref() } - } else { - this.timeout = null } + this.timeoutValue = delay } else if (this.timeout) { // istanbul ignore else: only for jest @@ -184,6 +203,8 @@ class Parser { this.timeout.refresh() } } + + this.timeoutType = type } resume () { @@ -624,7 +645,7 @@ function onParserTimeout (parser) { if (!paused) { util.destroy(socket, new BodyTimeoutError()) } - } else if (timeoutType === TIMEOUT_IDLE) { + } else if (timeoutType === TIMEOUT_KEEP_ALIVE) { assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue]) util.destroy(socket, new InformationalError('socket idle timeout')) } @@ -802,8 +823,8 @@ function resumeH1 (client) { } if (client[kSize] === 0) { - if (socket[kParser].timeoutType !== TIMEOUT_IDLE) { - socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_IDLE) + if (socket[kParser].timeoutType !== TIMEOUT_KEEP_ALIVE) { + socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_KEEP_ALIVE) } } else if (client[kRunning] > 0 && socket[kParser].statusCode < 200) { if (socket[kParser].timeoutType !== TIMEOUT_HEADERS) { diff --git a/lib/util/timers.js b/lib/util/timers.js index 8fa3ac56b7b..36b6bbf985e 100644 --- a/lib/util/timers.js +++ b/lib/util/timers.js @@ -347,7 +347,7 @@ module.exports = { * The clearTimeout method cancels an instantiated Timer previously created * by calling setTimeout. * - * @param {FastTimer} timeout + * @param {NodeJS.Timeout|FastTimer} timeout */ clearTimeout (timeout) { // If the timeout is a FastTimer, call its own clear method. @@ -362,6 +362,29 @@ module.exports = { nativeClearTimeout(timeout) } }, + /** + * The setFastTimeout() method sets a fastTimer which executes a function once + * the timer expires. + * @param {Function} callback A function to be executed after the timer + * expires. + * @param {number} delay The time, in milliseconds that the timer should + * wait before the specified function or code is executed. + * @param {*} [arg] An optional argument to be passed to the callback function + * when the timer expires. + * @returns {FastTimer} + */ + setFastTimeout (callback, delay, arg) { + return new FastTimer(callback, delay, arg) + }, + /** + * The clearTimeout method cancels an instantiated FastTimer previously + * created by calling setFastTimeout. + * + * @param {FastTimer} timeout + */ + clearFastTimeout (timeout) { + timeout.clear() + }, /** * The now method returns the value of the internal fast timer clock. * @@ -370,6 +393,30 @@ module.exports = { now () { return fastNow }, + /** + * Trigger the onTick function to process the fastTimers array. + * Exported for testing purposes only. + * Marking as deprecated to discourage any use outside of testing. + * @deprecated + * @param {number} [delay=0] The delay in milliseconds to add to the now value. + */ + tick (delay = 0) { + fastNow += delay - RESOLUTION_MS + 1 + onTick() + onTick() + }, + /** + * Reset FastTimers. + * Exported for testing purposes only. + * Marking as deprecated to discourage any use outside of testing. + * @deprecated + */ + reset () { + fastNow = 0 + fastTimers.length = 0 + clearTimeout(fastNowTimeout) + fastNowTimeout = null + }, /** * Exporting for testing purposes only. * Marking as deprecated to discourage any use outside of testing. diff --git a/test/connect-timeout.js b/test/connect-timeout.js index ff50eb777a6..186067f80ac 100644 --- a/test/connect-timeout.js +++ b/test/connect-timeout.js @@ -10,12 +10,13 @@ const skip = !!process.env.CITGM // Using describe instead of test to avoid the timeout describe('prioritize socket errors over timeouts', { skip }, async () => { - const t = tspl({ ...assert, after: () => {} }, { plan: 1 }) + const t = tspl({ ...assert, after: () => {} }, { plan: 2 }) const client = new Pool('http://foorbar.invalid:1234', { connectTimeout: 1 }) client.request({ method: 'GET', path: '/foobar' }) .then(() => t.fail()) .catch((err) => { + t.strictEqual(err.code, 'ENOTFOUND') t.strictEqual(err.code !== 'UND_ERR_CONNECT_TIMEOUT', true) }) @@ -32,7 +33,7 @@ net.connect = function (options) { } test('connect-timeout', { skip }, async t => { - t = tspl(t, { plan: 1 }) + t = tspl(t, { plan: 3 }) const client = new Client('http://localhost:9000', { connectTimeout: 1e3 @@ -48,6 +49,8 @@ test('connect-timeout', { skip }, async t => { method: 'GET' }, (err) => { t.ok(err instanceof errors.ConnectTimeoutError) + t.strictEqual(err.code, 'UND_ERR_CONNECT_TIMEOUT') + t.strictEqual(err.message, 'Connect Timeout Error (attempted address: localhost:9000, timeout: 1000ms)') clearTimeout(timeout) }) @@ -55,7 +58,7 @@ test('connect-timeout', { skip }, async t => { }) test('connect-timeout', { skip }, async t => { - t = tspl(t, { plan: 1 }) + t = tspl(t, { plan: 3 }) const client = new Pool('http://localhost:9000', { connectTimeout: 1e3 @@ -71,6 +74,8 @@ test('connect-timeout', { skip }, async t => { method: 'GET' }, (err) => { t.ok(err instanceof errors.ConnectTimeoutError) + t.strictEqual(err.code, 'UND_ERR_CONNECT_TIMEOUT') + t.strictEqual(err.message, 'Connect Timeout Error (attempted address: localhost:9000, timeout: 1000ms)') clearTimeout(timeout) }) diff --git a/test/issue-3356.js b/test/issue-3356.js index 927208583a9..fd7bf59656f 100644 --- a/test/issue-3356.js +++ b/test/issue-3356.js @@ -4,7 +4,7 @@ const { tspl } = require('@matteo.collina/tspl') const { test, after } = require('node:test') const { createServer } = require('node:http') const { once } = require('node:events') - +const { tick: fastTimersTick } = require('../lib/util/timers') const { fetch, Agent, RetryAgent } = require('..') test('https://github.com/nodejs/undici/issues/3356', async (t) => { @@ -42,6 +42,9 @@ test('https://github.com/nodejs/undici/issues/3356', async (t) => { const response = await fetch(`http://localhost:${server.address().port}`, { dispatcher: agent }) + + fastTimersTick() + setTimeout(async () => { try { t.equal(response.status, 200) diff --git a/test/request-timeout.js b/test/request-timeout.js index 03d34c9bef5..dfe0de1967c 100644 --- a/test/request-timeout.js +++ b/test/request-timeout.js @@ -1,11 +1,11 @@ 'use strict' const { tspl } = require('@matteo.collina/tspl') -const { test, after } = require('node:test') +const { resolve: pathResolve } = require('node:path') +const { test, after, beforeEach } = require('node:test') const { createReadStream, writeFileSync, unlinkSync } = require('node:fs') const { Client, errors } = require('..') const { kConnect } = require('../lib/core/symbols') -const timers = require('../lib/util/timers') const { createServer } = require('node:http') const EventEmitter = require('node:events') const FakeTimers = require('@sinonjs/fake-timers') @@ -16,6 +16,14 @@ const { Writable, PassThrough } = require('node:stream') +const { + tick: fastTimersTick, + reset: resetFastTimers +} = require('../lib/util/timers') + +beforeEach(() => { + resetFastTimers() +}) test('request timeout', async (t) => { t = tspl(t, { plan: 1 }) @@ -46,7 +54,7 @@ test('request timeout with readable body', async (t) => { }) after(() => server.close()) - const tempfile = `${__filename}.10mb.txt` + const tempfile = pathResolve(__dirname, 'request-timeout.10mb.bin') writeFileSync(tempfile, Buffer.alloc(10 * 1024 * 1024)) after(() => unlinkSync(tempfile)) @@ -72,12 +80,6 @@ test('body timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { res.write('hello') }) @@ -91,12 +93,14 @@ test('body timeout', async (t) => { t.ifError(err) body.on('data', () => { clock.tick(100) + fastTimersTick(100) }).on('error', (err) => { t.ok(err instanceof errors.BodyTimeoutError) }) }) clock.tick(50) + fastTimersTick(50) }) await t.completed @@ -111,17 +115,12 @@ test('overridden request timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 100) clock.tick(100) + fastTimersTick(100) }) after(() => server.close()) @@ -134,6 +133,7 @@ test('overridden request timeout', async (t) => { }) clock.tick(50) + fastTimersTick(50) }) await t.completed @@ -148,12 +148,6 @@ test('overridden body timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { res.write('hello') }) @@ -166,13 +160,15 @@ test('overridden body timeout', async (t) => { client.request({ path: '/', method: 'GET', bodyTimeout: 50 }, (err, { body }) => { t.ifError(err) body.on('data', () => { - clock.tick(100) + fastTimersTick() + fastTimersTick() }).on('error', (err) => { t.ok(err instanceof errors.BodyTimeoutError) }) }) - clock.tick(50) + fastTimersTick() + fastTimersTick() }) await t.completed @@ -187,17 +183,12 @@ test('With EE signal', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 100) clock.tick(100) + fastTimersTick(100) }) after(() => server.close()) @@ -213,6 +204,7 @@ test('With EE signal', async (t) => { }) clock.tick(50) + fastTimersTick(50) }) await t.completed @@ -227,17 +219,12 @@ test('With abort-controller signal', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 100) clock.tick(100) + fastTimersTick(100) }) after(() => server.close()) @@ -253,6 +240,7 @@ test('With abort-controller signal', async (t) => { }) clock.tick(50) + fastTimersTick(50) }) await t.completed @@ -267,12 +255,6 @@ test('Abort before timeout (EE)', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const ee = new EventEmitter() const server = createServer((req, res) => { setTimeout(() => { @@ -280,6 +262,7 @@ test('Abort before timeout (EE)', async (t) => { }, 100) ee.emit('abort') clock.tick(50) + fastTimersTick(50) }) after(() => server.close()) @@ -292,6 +275,7 @@ test('Abort before timeout (EE)', async (t) => { client.request({ path: '/', method: 'GET', signal: ee }, (err, response) => { t.ok(err instanceof errors.RequestAbortedError) clock.tick(100) + fastTimersTick(100) }) }) @@ -307,12 +291,6 @@ test('Abort before timeout (abort-controller)', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const abortController = new AbortController() const server = createServer((req, res) => { setTimeout(() => { @@ -320,6 +298,7 @@ test('Abort before timeout (abort-controller)', async (t) => { }, 100) abortController.abort() clock.tick(50) + fastTimersTick(50) }) after(() => server.close()) @@ -332,6 +311,7 @@ test('Abort before timeout (abort-controller)', async (t) => { client.request({ path: '/', method: 'GET', signal: abortController.signal }, (err, response) => { t.ok(err instanceof errors.RequestAbortedError) clock.tick(100) + fastTimersTick(100) }) }) @@ -347,17 +327,12 @@ test('Timeout with pipelining', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 100) clock.tick(50) + fastTimersTick(50) }) after(() => server.close()) @@ -393,17 +368,12 @@ test('Global option', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 100) clock.tick(100) + fastTimersTick(100) }) after(() => server.close()) @@ -418,6 +388,7 @@ test('Global option', async (t) => { }) clock.tick(50) + fastTimersTick(50) }) await t.completed @@ -432,17 +403,12 @@ test('Request options overrides global option', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 100) clock.tick(100) + fastTimersTick(100) }) after(() => server.close()) @@ -457,6 +423,7 @@ test('Request options overrides global option', async (t) => { }) clock.tick(50) + fastTimersTick(50) }) await t.completed @@ -496,12 +463,6 @@ test('client.close should wait for the timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { }) after(() => server.close()) @@ -523,6 +484,7 @@ test('client.close should wait for the timeout', async (t) => { client.on('connect', () => { process.nextTick(() => { clock.tick(100) + fastTimersTick(100) }) }) }) @@ -581,17 +543,12 @@ test('Disable request timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 32e3) clock.tick(33e3) + fastTimersTick(33e3) }) after(() => server.close()) @@ -614,6 +571,7 @@ test('Disable request timeout', async (t) => { }) clock.tick(31e3) + fastTimersTick(31e3) }) await t.completed @@ -628,17 +586,12 @@ test('Disable request timeout for a single request', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 32e3) clock.tick(33e3) + fastTimersTick(33e3) }) after(() => server.close()) @@ -661,6 +614,7 @@ test('Disable request timeout for a single request', async (t) => { }) clock.tick(31e3) + fastTimersTick(31e3) }) await t.completed @@ -675,17 +629,12 @@ test('stream timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 301e3) clock.tick(301e3) + fastTimersTick(301e3) }) after(() => server.close()) @@ -716,17 +665,12 @@ test('stream custom timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { res.end('hello') }, 31e3) clock.tick(31e3) + fastTimersTick(31e3) }) after(() => server.close()) @@ -759,17 +703,12 @@ test('pipeline timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { req.pipe(res) }, 301e3) clock.tick(301e3) + fastTimersTick(301e3) }) after(() => server.close()) @@ -819,17 +758,12 @@ test('pipeline timeout', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { setTimeout(() => { req.pipe(res) }, 31e3) clock.tick(31e3) + fastTimersTick(31e3) }) after(() => server.close()) @@ -881,12 +815,6 @@ test('client.close should not deadlock', async (t) => { }) after(() => clock.uninstall()) - const orgTimers = { ...timers } - Object.assign(timers, { setTimeout, clearTimeout }) - after(() => { - Object.assign(timers, orgTimers) - }) - const server = createServer((req, res) => { }) after(() => server.close()) @@ -911,6 +839,7 @@ test('client.close should not deadlock', async (t) => { }) clock.tick(100) + fastTimersTick(100) }) }) await t.completed