diff --git a/lib/api/abort-signal.js b/lib/api/abort-signal.js index 2afe747d3a1..0cd020ba200 100644 --- a/lib/api/abort-signal.js +++ b/lib/api/abort-signal.js @@ -1,8 +1,9 @@ const { addAbortListener } = require('../core/util') const { RequestAbortedError } = require('../core/errors') -const kListener = Symbol('kListener') +const kListenersList = Symbol('kListenersList') const kSignal = Symbol('kSignal') +const kHandle = Symbol('kHandle') function abort (self) { if (self.abort) { @@ -13,11 +14,25 @@ function abort (self) { removeSignal(self) } +function clearListeners (signal) { + if (!signal[kListenersList]) { + return + } + if ('removeEventListener' in signal) { + signal.removeEventListener('abort', signal[kHandle]) + } else { + signal.removeListener('abort', signal[kHandle]) + } + if (signal[kListenersList].size !== 0) { + signal[kListenersList].clear() + } + signal[kListenersList] = null +} + function addSignal (self, signal) { self.reason = null self[kSignal] = null - self[kListener] = null if (!signal) { return @@ -29,11 +44,19 @@ function addSignal (self, signal) { } self[kSignal] = signal - self[kListener] = () => { - abort(self) + + if (!signal[kListenersList]) { + signal[kListenersList] = new Set() + signal[kHandle] = () => { + for (const listener of signal[kListenersList]) { + abort(listener) + } + clearListeners(signal) + } + addAbortListener(signal, signal[kHandle]) } - addAbortListener(self[kSignal], self[kListener]) + signal[kListenersList].add(self) } function removeSignal (self) { @@ -41,14 +64,13 @@ function removeSignal (self) { return } - if ('removeEventListener' in self[kSignal]) { - self[kSignal].removeEventListener('abort', self[kListener]) - } else { - self[kSignal].removeListener('abort', self[kListener]) + self[kSignal][kListenersList].delete(self) + + if (self[kSignal][kListenersList].size === 0) { + clearListeners(self[kSignal]) } self[kSignal] = null - self[kListener] = null } module.exports = { diff --git a/test/issue-3131.js b/test/issue-3131.js new file mode 100644 index 00000000000..02c1f501b11 --- /dev/null +++ b/test/issue-3131.js @@ -0,0 +1,43 @@ +'use strict' + +const { createServer } = require('node:http') +const { EventEmitter, once } = require('node:events') +const { request } = require('../') +const { test } = require('node:test') +const { closeServerAsPromise } = require('./utils/node-http') +const { strictEqual } = require('node:assert') + +test('issue #3131', async (t) => { + const emitter = new EventEmitter() + + const server = createServer((req, res) => { + res.end('Hi') + }) + + server.listen(0) + + await once(server, 'listening') + + t.after(closeServerAsPromise(server)) + + const url = `http://localhost:${server.address().port}` + + let warningEmitted = false + function onWarning () { + warningEmitted = true + } + process.on('warning', onWarning) + t.after(() => { + process.off('warning', onWarning) + }) + + const promises = new Array(50) + + for (let i = 0; i < promises.length; ++i) { + promises[i] = request(url, { signal: emitter }) + } + + await Promise.all(promises) + + strictEqual(warningEmitted, false) +})