diff --git a/lib/dgram.js b/lib/dgram.js index 39d43092e15832..1fd83094de2e7b 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -282,13 +282,27 @@ function enqueue(self, toEnqueue) { // event handler that flushes the send queue after binding is done. if (!self._queue) { self._queue = []; - self.once('listening', clearQueue); + self.once('error', onListenError); + self.once('listening', onListenSuccess); } self._queue.push(toEnqueue); return; } +function onListenSuccess() { + this.removeListener('error', onListenError); + clearQueue.call(this); +} + + +function onListenError(err) { + this.removeListener('listening', onListenSuccess); + this._queue = undefined; + this.emit('error', new Error('Unable to send data')); +} + + function clearQueue() { const queue = this._queue; this._queue = undefined; diff --git a/test/parallel/test-dgram-implicit-bind-failure.js b/test/parallel/test-dgram-implicit-bind-failure.js new file mode 100644 index 00000000000000..764b1fc1c9d69e --- /dev/null +++ b/test/parallel/test-dgram-implicit-bind-failure.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common'); +const assert = require('assert'); +const dgram = require('dgram'); +const dns = require('dns'); + +// Monkey patch dns.lookup() so that it always fails. +dns.lookup = function(address, family, callback) { + process.nextTick(() => { callback(new Error('fake DNS')); }); +}; + +const socket = dgram.createSocket('udp4'); +let dnsFailures = 0; +let sendFailures = 0; + +process.on('exit', () => { + assert.strictEqual(dnsFailures, 3); + assert.strictEqual(sendFailures, 3); +}); + +socket.on('error', (err) => { + if (/^Error: fake DNS$/.test(err)) { + // The DNS lookup should fail since it is monkey patched. At that point in + // time, the send queue should be populated with the send() operation. There + // should also be two listeners - this function and the dgram internal one + // time error handler. + dnsFailures++; + assert(Array.isArray(socket._queue)); + assert.strictEqual(socket._queue.length, 1); + assert.strictEqual(socket.listenerCount('error'), 2); + return; + } + + if (/^Error: Unable to send data$/.test(err)) { + // On error, the queue should be destroyed and this function should be + // the only listener. + sendFailures++; + assert.strictEqual(socket._queue, undefined); + assert.strictEqual(socket.listenerCount('error'), 1); + return; + } + + common.fail(`Unexpected error: ${err}`); +}); + +// Initiate a few send() operations, which will fail. +socket.send('foobar', common.PORT, 'localhost'); + +process.nextTick(() => { + socket.send('foobar', common.PORT, 'localhost'); +}); + +setImmediate(() => { + socket.send('foobar', common.PORT, 'localhost'); +});