From 3d39be73d470ca57094ca8c98be5e2ee7126ab66 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Fri, 1 Nov 2019 19:50:40 +0100 Subject: [PATCH] dgram: remove listeners on bind error This avoids piling up `'listening'` event listeners if `.bind()` fails repeatedly. Fixes: https://github.com/nodejs/node/issues/30209 PR-URL: https://github.com/nodejs/node/pull/30210 Reviewed-By: Luigi Pinca Reviewed-By: Colin Ihrig Reviewed-By: James M Snell --- lib/dgram.js | 17 ++++++++++-- test/parallel/test-dgram-bind-error-repeat.js | 27 +++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-dgram-bind-error-repeat.js diff --git a/lib/dgram.js b/lib/dgram.js index c365df2414f99d..ae260ba437bf54 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -211,8 +211,21 @@ Socket.prototype.bind = function(port_, address_ /* , callback */) { state.bindState = BIND_STATE_BINDING; - if (arguments.length && typeof arguments[arguments.length - 1] === 'function') - this.once('listening', arguments[arguments.length - 1]); + const cb = arguments.length && arguments[arguments.length - 1]; + if (typeof cb === 'function') { + function removeListeners() { + this.removeListener('error', removeListeners); + this.removeListener('listening', onListening); + } + + function onListening() { + removeListeners.call(this); + cb.call(this); + } + + this.on('error', removeListeners); + this.on('listening', onListening); + } if (port instanceof UDP) { replaceHandle(this, port); diff --git a/test/parallel/test-dgram-bind-error-repeat.js b/test/parallel/test-dgram-bind-error-repeat.js new file mode 100644 index 00000000000000..a520d30a519cd2 --- /dev/null +++ b/test/parallel/test-dgram-bind-error-repeat.js @@ -0,0 +1,27 @@ +'use strict'; +const common = require('../common'); +const dgram = require('dgram'); + +// Regression test for https://github.com/nodejs/node/issues/30209 +// No warning should be emitted when re-trying `.bind()` on UDP sockets +// repeatedly. + +process.on('warning', common.mustNotCall()); + +const reservePortSocket = dgram.createSocket('udp4'); +reservePortSocket.bind(() => { + const { port } = reservePortSocket.address(); + + const newSocket = dgram.createSocket('udp4'); + + let errors = 0; + newSocket.on('error', common.mustCall(() => { + if (++errors < 20) { + newSocket.bind(port, common.mustNotCall()); + } else { + newSocket.close(); + reservePortSocket.close(); + } + }, 20)); + newSocket.bind(port, common.mustNotCall()); +});