diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 6e2a430840a9..176071026e22 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -228,6 +228,7 @@ function onocspresponse(resp) { function TLSSocket(socket, options) { // Disallow wrapping TLSSocket in TLSSocket assert(!(socket instanceof TLSSocket)); + var self = this; net.Socket.call(this, { handle: socket && socket._handle, @@ -238,6 +239,9 @@ function TLSSocket(socket, options) { if (socket) { this._parent = socket; + socket._destroy = function(exception) { + self._destroy(exception) + }; // To prevent assertion in afterConnect() this._connecting = socket._connecting; } diff --git a/lib/net.js b/lib/net.js index e6e48f4d9fd8..a0c167eaee2a 100644 --- a/lib/net.js +++ b/lib/net.js @@ -456,7 +456,8 @@ Socket.prototype._destroy = function(exception, cb) { if (cb) cb(exception); if (exception && !self._writableState.errorEmitted) { process.nextTick(function() { - self.emit('error', exception); + for (var s = self; s !== null; s = s._parent) + s.emit('error', exception); }); self._writableState.errorEmitted = true; } @@ -468,12 +469,10 @@ Socket.prototype._destroy = function(exception, cb) { return; } - self._connecting = false; - - this.readable = this.writable = false; - - for (var s = this; s !== null; s = s._parent) + for (var s = this; s !== null; s = s._parent) { timers.unenroll(s); + s._connecting = s.readable = s.writable = false; + } debug('close'); if (this._handle) { @@ -482,16 +481,20 @@ Socket.prototype._destroy = function(exception, cb) { var isException = exception ? true : false; this._handle.close(function() { debug('emit close'); - self.emit('close', isException); + for (var s = self; s !== null; s = s._parent) + s.emit('close', isException); }); this._handle.onread = noop; - this._handle = null; + for (var s = this; s !== null; s = s._parent) + s._handle = null; } // we set destroyed to true before firing error callbacks in order // to make it re-entrance safe in case Socket.prototype.destroy() // is called within callbacks - this.destroyed = true; + for (var s = this; s !== null; s = s._parent) + s.destroyed = true; + fireErrorCallbacks(); if (this.server) { diff --git a/test/simple/test-tls-destroy-socket.js b/test/simple/test-tls-destroy-socket.js new file mode 100644 index 000000000000..c79d24822711 --- /dev/null +++ b/test/simple/test-tls-destroy-socket.js @@ -0,0 +1,55 @@ +if (!process.versions.openssl) { + console.error('Skipping because node compiled without OpenSSL.'); + process.exit(0); +} + +var common = require('../common'); +var assert = require('assert'); +var tls = require('tls'); +var fs = require('fs'); + +var options = { + key: fs.readFileSync(common.fixturesDir + '/keys/agent2-key.pem'), + cert: fs.readFileSync(common.fixturesDir + '/keys/agent2-cert.pem') +}; + +var normalSock = null; +var hasError = false; +var tlsCloseCount = 0; +var sockCloseCount = 0; + +var server = tls.createServer(options, function(secureSock) { + secureSock.on('error', function() { + hasError = true; + }); + secureSock.on('close', function() { + tlsCloseCount++; + }); + normalSock.on('close', function() { + sockCloseCount++; + }); + + normalSock.destroy(); + secureSock.write('Test!', function(err) { + assert(err); + }); +}); + +server.on('connection', function(sock) { + normalSock = sock; +}); + +server.listen(common.PORT, function() { + var c = tls.connect(common.PORT, {rejectUnauthorized: false}); + c.on('error', function() {}); // ignore socket hangup error + + c.on('end', function() { + server.close(); + }); + + process.on('exit', function() { + assert(hasError) + assert.equal(tlsCloseCount, 1); + assert.equal(sockCloseCount, 1); + }); +});