net: persist net.Socket options before connect
authorEvan Lucas <evanlucas@me.com>
Wed, 18 Feb 2015 18:02:01 +0000 (12:02 -0600)
committerEvan Lucas <evanlucas@me.com>
Tue, 19 May 2015 18:21:44 +0000 (13:21 -0500)
Remembers net.Socket options called before connect and retroactively
applies them after the handle has been created.

This change makes the following function calls more user-friendly:

- setKeepAlive()
- setNoDelay()
- ref()
- unref()

Related: https://github.com/joyent/node/issues/7077 and
https://github.com/joyent/node/issues/8572

Fixes: https://github.com/joyent/node/issues/7077
Fixes: https://github.com/joyent/node/issues/8572
PR-URL: https://github.com/nodejs/io.js/pull/1518
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Roman Reiss <me@silverwind.io>
lib/net.js
test/parallel/test-net-persistent-keepalive.js [new file with mode: 0644]
test/parallel/test-net-persistent-nodelay.js [new file with mode: 0644]
test/parallel/test-net-persistent-ref-unref.js [new file with mode: 0644]

index f8ebb2c..76c6f1d 100644 (file)
@@ -321,14 +321,25 @@ Socket.prototype._onTimeout = function() {
 
 
 Socket.prototype.setNoDelay = function(enable) {
+  if (!this._handle) {
+    this.once('connect',
+              enable ? this.setNoDelay : this.setNoDelay.bind(this, enable));
+    return;
+  }
+
   // backwards compatibility: assume true when `enable` is omitted
-  if (this._handle && this._handle.setNoDelay)
+  if (this._handle.setNoDelay)
     this._handle.setNoDelay(enable === undefined ? true : !!enable);
 };
 
 
 Socket.prototype.setKeepAlive = function(setting, msecs) {
-  if (this._handle && this._handle.setKeepAlive)
+  if (!this._handle) {
+    this.once('connect', this.setKeepAlive.bind(this, setting, msecs));
+    return;
+  }
+
+  if (this._handle.setKeepAlive)
     this._handle.setKeepAlive(setting, ~~(msecs / 1000));
 };
 
@@ -971,14 +982,22 @@ function connectErrorNT(self, err, options) {
 
 
 Socket.prototype.ref = function() {
-  if (this._handle)
-    this._handle.ref();
+  if (!this._handle) {
+    this.once('connect', this.ref);
+    return;
+  }
+
+  this._handle.ref();
 };
 
 
 Socket.prototype.unref = function() {
-  if (this._handle)
-    this._handle.unref();
+  if (!this._handle) {
+    this.once('connect', this.unref);
+    return;
+  }
+
+  this._handle.unref();
 };
 
 
diff --git a/test/parallel/test-net-persistent-keepalive.js b/test/parallel/test-net-persistent-keepalive.js
new file mode 100644 (file)
index 0000000..adf7d7a
--- /dev/null
@@ -0,0 +1,33 @@
+'use strict';
+var common = require('../common');
+var assert = require('assert');
+var net = require('net');
+
+var serverConnection;
+var echoServer = net.createServer(function(connection) {
+  serverConnection = connection;
+  connection.setTimeout(0);
+  assert.equal(typeof connection.setKeepAlive, 'function');
+  connection.on('end', function() {
+    connection.end();
+  });
+});
+echoServer.listen(common.PORT);
+
+echoServer.on('listening', function() {
+  var clientConnection = new net.Socket();
+  // send a keepalive packet after 1000 ms
+  // and make sure it persists
+  clientConnection.setKeepAlive(true, 400);
+  clientConnection.connect(common.PORT);
+  clientConnection.setTimeout(0);
+
+  setTimeout(function() {
+    // make sure both connections are still open
+    assert.equal(serverConnection.readyState, 'open');
+    assert.equal(clientConnection.readyState, 'open');
+    serverConnection.end();
+    clientConnection.end();
+    echoServer.close();
+  }, 600);
+});
diff --git a/test/parallel/test-net-persistent-nodelay.js b/test/parallel/test-net-persistent-nodelay.js
new file mode 100644 (file)
index 0000000..8ed5925
--- /dev/null
@@ -0,0 +1,33 @@
+'use strict';
+var common = require('../common');
+var assert = require('assert');
+var net = require('net');
+var TCPWrap = process.binding('tcp_wrap').TCP;
+
+var echoServer = net.createServer(function(connection) {
+  connection.end();
+});
+echoServer.listen(common.PORT);
+
+var callCount = 0;
+
+var Socket = net.Socket;
+var setNoDelay = TCPWrap.prototype.setNoDelay;
+
+TCPWrap.prototype.setNoDelay = function(enable) {
+  setNoDelay.call(this, enable);
+  callCount++;
+};
+
+echoServer.on('listening', function() {
+  var sock1 = new Socket();
+  // setNoDelay before the handle is created
+  // there is probably a better way to test this
+
+  sock1.setNoDelay();
+  sock1.connect(common.PORT);
+  sock1.on('end', function() {
+    assert.equal(callCount, 1);
+    echoServer.close();
+  });
+});
diff --git a/test/parallel/test-net-persistent-ref-unref.js b/test/parallel/test-net-persistent-ref-unref.js
new file mode 100644 (file)
index 0000000..b3ea096
--- /dev/null
@@ -0,0 +1,39 @@
+'use strict';
+var common = require('../common');
+var assert = require('assert');
+var net = require('net');
+var TCPWrap = process.binding('tcp_wrap').TCP;
+
+var echoServer = net.createServer(function(conn) {
+  conn.end();
+});
+
+var ref = TCPWrap.prototype.ref;
+var unref = TCPWrap.prototype.unref;
+
+var refCount = 0;
+
+TCPWrap.prototype.ref = function() {
+  ref.call(this);
+  refCount++;
+  assert.equal(refCount, 0);
+};
+
+TCPWrap.prototype.unref = function() {
+  unref.call(this);
+  refCount--;
+  assert.equal(refCount, -1);
+};
+
+echoServer.listen(common.PORT);
+
+echoServer.on('listening', function() {
+  var sock = new net.Socket();
+  sock.unref();
+  sock.ref();
+  sock.connect(common.PORT);
+  sock.on('end', function() {
+    assert.equal(refCount, 0);
+    echoServer.close();
+  });
+});