net: use `_server` for internal book-keeping
authorFedor Indutny <fedor@indutny.com>
Tue, 16 Feb 2016 20:09:31 +0000 (15:09 -0500)
committerMyles Borins <mborins@us.ibm.com>
Wed, 2 Mar 2016 22:01:11 +0000 (14:01 -0800)
The role of `this.server` is now split between `this._server` and
`this.server`. Where the first one is used for counting active
connections of `net.Server`, and the latter one is just a public API for
users' consumption.

The reasoning for this is simple, `TLSSocket` instances wrap
`net.Socket` instances, thus both refer to the `net.Server` through the
`this.server` property. However, only one of them should be used for
`net.Server` connection count book-keeping, otherwise double-decrement
will happen on socket destruction.

Fix: #5083
PR-URL: https://github.com/nodejs/node/pull/5262
Reviewed-By: James M Snell <jasnell@gmail.com>
lib/_tls_wrap.js
lib/net.js
test/parallel/test-net-stream.js
test/parallel/test-tls-server-connection-server.js [new file with mode: 0644]

index 2077bf0..39d9198 100644 (file)
@@ -392,12 +392,6 @@ TLSSocket.prototype._init = function(socket, wrap) {
 
   this.server = options.server;
 
-  // Move the server to TLSSocket, otherwise both `socket.destroy()` and
-  // `TLSSocket.destroy()` will decrement number of connections of the TLS
-  // server, leading to misfiring `server.close()` callback
-  if (socket && socket.server === this.server)
-    socket.server = null;
-
   // For clients, we will always have either a given ca list or be using
   // default one
   const requestCert = !!options.requestCert || !options.isServer;
index 157b197..4ecc4e2 100644 (file)
@@ -171,6 +171,10 @@ function Socket(options) {
       this.read(0);
     }
   }
+
+  // Reserve properties
+  this.server = null;
+  this._server = null;
 }
 util.inherits(Socket, stream.Duplex);
 
@@ -481,12 +485,12 @@ Socket.prototype._destroy = function(exception, cb) {
   this.destroyed = true;
   fireErrorCallbacks();
 
-  if (this.server) {
+  if (this._server) {
     COUNTER_NET_SERVER_CONNECTION_CLOSE(this);
     debug('has server');
-    this.server._connections--;
-    if (this.server._emitCloseIfDrained) {
-      this.server._emitCloseIfDrained();
+    this._server._connections--;
+    if (this._server._emitCloseIfDrained) {
+      this._server._emitCloseIfDrained();
     }
   }
 };
@@ -1416,6 +1420,7 @@ function onconnection(err, clientHandle) {
 
   self._connections++;
   socket.server = self;
+  socket._server = self;
 
   DTRACE_NET_SERVER_CONNECTION(socket);
   LTTNG_NET_SERVER_CONNECTION(socket);
index f2b795f..9075d99 100644 (file)
@@ -11,6 +11,7 @@ var s = new net.Stream();
 
 s.server = new net.Server();
 s.server.connections = 10;
+s._server = s.server;
 
 assert.equal(10, s.server.connections);
 s.destroy();
diff --git a/test/parallel/test-tls-server-connection-server.js b/test/parallel/test-tls-server-connection-server.js
new file mode 100644 (file)
index 0000000..534a16a
--- /dev/null
@@ -0,0 +1,34 @@
+'use strict';
+const common = require('../common');
+
+if (!common.hasCrypto) {
+  console.log('1..0 # Skipped: missing crypto');
+  return;
+}
+
+const assert = require('assert');
+const tls = require('tls');
+const fs = require('fs');
+
+const options = {
+  key: fs.readFileSync(common.fixturesDir + '/keys/agent1-key.pem'),
+  cert: fs.readFileSync(common.fixturesDir + '/keys/agent1-cert.pem')
+};
+
+const server = tls.createServer(options, function(s) {
+  s.end('hello');
+}).listen(common.PORT, function() {
+  const opts = {
+    port: common.PORT,
+    rejectUnauthorized: false
+  };
+
+  server.on('connection', common.mustCall(function(socket) {
+    assert(socket.server === server);
+    server.close();
+  }));
+
+  const client = tls.connect(opts, function() {
+    client.end();
+  });
+});