diff --git a/doc/api.markdown b/doc/api.markdown index 274d8f611ec..761c072586f 100644 --- a/doc/api.markdown +++ b/doc/api.markdown @@ -2042,6 +2042,15 @@ Disables the Nagle algorithm. By default TCP connections use the Nagle algorithm, they buffer data before sending it off. Setting `noDelay` will immediately fire off data each time `stream.write()` is called. +### stream.setKeepAlive(enable=false, initialDelay) + +Enable/disable keep-alive functionality, and optionally set the initial +delay before the first keepalive probe is sent on an idle stream. +Set `initialDelay` (in milliseconds) to set the delay between the last +data packet received and the first keepalive probe. Setting 0 for +initialDelay will leave the value unchanged from the default +(or previous) setting. + ## DNS diff --git a/lib/net.js b/lib/net.js index 125582f8a2c..205b26b80c8 100644 --- a/lib/net.js +++ b/lib/net.js @@ -38,6 +38,7 @@ var read = binding.read; var write = binding.write; var toRead = binding.toRead; var setNoDelay = binding.setNoDelay; +var setKeepAlive= binding.setKeepAlive; var socketError = binding.socketError; var getsockname = binding.getsockname; var errnoException = binding.errnoException; @@ -744,9 +745,17 @@ Stream.prototype.address = function () { Stream.prototype.setNoDelay = function (v) { - if (this.type == 'tcp') setNoDelay(this.fd, v); + if ((this.type == 'tcp4')||(this.type == 'tcp6')) { + setNoDelay(this.fd, v); + } }; +Stream.prototype.setKeepAlive = function (enable, time) { + if ((this.type == 'tcp4')||(this.type == 'tcp6')) { + var secondDelay = Math.ceil(time/1000); + setKeepAlive(this.fd, enable, secondDelay); + } +}; Stream.prototype.setTimeout = function (msecs) { timeout.enroll(this, msecs); diff --git a/src/node_net2.cc b/src/node_net2.cc index eb6ea1aa78b..1069fc9ab43 100644 --- a/src/node_net2.cc +++ b/src/node_net2.cc @@ -1062,11 +1062,43 @@ static Handle SetNoDelay(const Arguments& args) { if (r < 0) { return ThrowException(ErrnoException(errno, "setsockopt")); } - return Undefined(); } +static Handle SetKeepAlive(const Arguments& args) { + int r; + HandleScope scope; + + bool enable = false; + int time = 0; + + FD_ARG(args[0]) + + if (args.Length() > 0) enable = args[1]->IsTrue(); + if (enable == true) { + time = args[2]->Int32Value(); + } + + int flags = enable ? 1 : 0; + r = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&flags, sizeof(flags)); + if ((time > 0)&&(r >= 0)) { +#if defined(__APPLE__) + // Mac uses a different setting name than Linux + r = setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, (void *)&time, sizeof(time)); +#elif defined(__sun) + // Solaris doesn't support TCP_KEEPIDLE, so do nothing here +#else + // assume anything else uses the Linux/BSD method + r = setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&time, sizeof(time)); +#endif + } + if (r < 0) { + return ThrowException(ErrnoException(errno, "setsockopt")); + } + return Undefined(); +} + // // G E T A D D R I N F O // @@ -1287,6 +1319,7 @@ void InitNet2(Handle target) { NODE_SET_METHOD(target, "socketError", SocketError); NODE_SET_METHOD(target, "toRead", ToRead); NODE_SET_METHOD(target, "setNoDelay", SetNoDelay); + NODE_SET_METHOD(target, "setKeepAlive", SetKeepAlive); NODE_SET_METHOD(target, "getsockname", GetSockName); NODE_SET_METHOD(target, "getpeername", GetPeerName); NODE_SET_METHOD(target, "getaddrinfo", GetAddrInfo); diff --git a/test/simple/test-tcp-keepalive.js b/test/simple/test-tcp-keepalive.js new file mode 100644 index 00000000000..adf532663e3 --- /dev/null +++ b/test/simple/test-tcp-keepalive.js @@ -0,0 +1,28 @@ +require("../common"); +net = require('net'); + +var serverConnection; +var echoServer = net.createServer(function (connection) { + serverConnection = connection; + connection.setTimeout(0); + assert.notEqual(connection.setKeepAlive,undefined); + // send a keepalive packet after 1000 ms + connection.setKeepAlive(true,1000); + connection.addListener("end", function () { + connection.end(); + }); +}); +echoServer.listen(PORT); + +var clientConnection = net.createConnection(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(); +}, 1200); +