add library support for tuning TCP_KEEPALIVE
authorDave Reisner <dreisner@archlinux.org>
Tue, 24 Jan 2012 01:28:06 +0000 (01:28 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 9 Feb 2012 17:53:51 +0000 (18:53 +0100)
This adds three new options to control the behavior of TCP keepalives:

- CURLOPT_TCP_KEEPALIVE: enable/disable probes
- CURLOPT_TCP_KEEPIDLE: idle time before sending first probe
- CURLOPT_TCP_KEEPINTVL: delay between successive probes

While not all operating systems support the TCP_KEEPIDLE and
TCP_KEEPINTVL knobs, the library will still allow these options to be
set by clients, silently ignoring the values.

docs/libcurl/curl_easy_setopt.3
docs/libcurl/symbols-in-versions
include/curl/curl.h
lib/connect.c
lib/url.c
lib/urldata.h

index d94a84b..a7688a7 100644 (file)
@@ -915,6 +915,24 @@ overdone.
 .IP CURLOPT_ADDRESS_SCOPE
 Pass a long specifying the scope_id value to use when connecting to IPv6
 link-local or site-local addresses. (Added in 7.19.0)
+
+.IP CURLOPT_TCP_KEEPALIVE
+Pass a long. If set to 1, TCP keepalive probes will be sent. The delay and
+frequency of these probes can be controlled by the \fICURLOPT_TCP_KEEPIDLE\fP
+and \fICURLOPT_TCP_KEEPINTVL\fP options, provided the operating system supports
+them. Set to 0 (default behavior) to disable keepalive probes (Added in
+7.24.1).
+
+.IP CURLOPT_TCP_KEEPIDLE
+Pass a long. Sets the delay, in seconds, that the operating system will wait
+while the connection is idle before sending keepalive probes. Not all operating
+systems support this option. (Added in 7.24.1)
+
+.IP CURLOPT_TCP_KEEPINTVL
+Pass a long. Sets the interval, in seconds, that the operating system will wait
+between sending keepalive probes. Not all operating systems support this
+option. (Added in 7.24.1)
+
 .SH NAMES and PASSWORDS OPTIONS (Authentication)
 .IP CURLOPT_NETRC
 This parameter controls the preference of libcurl between using user names and
index 73d50a2..a9f8a9d 100644 (file)
@@ -486,6 +486,9 @@ CURLOPT_SSL_SESSIONID_CACHE     7.16.0
 CURLOPT_SSL_VERIFYHOST          7.8.1
 CURLOPT_SSL_VERIFYPEER          7.4.2
 CURLOPT_STDERR                  7.1
+CURLOPT_TCP_KEEPALIVE           7.24.1
+CURLOPT_TCP_KEEPIDLE            7.24.1
+CURLOPT_TCP_KEEPINTVL           7.24.1
 CURLOPT_TCP_NODELAY             7.11.2
 CURLOPT_TELNETOPTIONS           7.7
 CURLOPT_TFTP_BLKSIZE            7.19.4
index 59a5c79..2a9957e 100644 (file)
@@ -1499,6 +1499,13 @@ typedef enum {
      of miliseconds. */
   CINIT(ACCEPTTIMEOUT_MS, LONG, 212),
 
+  /* Set TCP keepalive */
+  CINIT(TCP_KEEPALIVE, LONG, 213),
+
+  /* non-universal keepalive knobs (Linux, AIX, HP-UX, more) */
+  CINIT(TCP_KEEPIDLE, LONG, 214),
+  CINIT(TCP_KEEPINTVL, LONG, 215),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
index b5082d8..5747b04 100644 (file)
 
 static bool verifyconnect(curl_socket_t sockfd, int *error);
 
+static void
+tcpkeepalive(struct SessionHandle *data,
+             int sockfd)
+{
+  int optval = data->set.tcp_keepalive;
+
+  /* only set IDLE and INTVL if setting KEEPALIVE is successful */
+  if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
+        (void *)&optval, sizeof(optval)) < 0) {
+    infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd);
+  }
+  else {
+#ifdef TCP_KEEPIDLE
+    optval = data->set.tcp_keepidle;
+    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
+          (void *)&optval, sizeof(optval)) < 0) {
+      infof(data, "Failed to set TCP_KEEPIDLE on fd %d\n", sockfd);
+    }
+#endif
+#ifdef TCP_KEEPINTVL
+    optval = data->set.tcp_keepintvl;
+    if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
+          (void *)&optval, sizeof(optval)) < 0) {
+      infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd);
+    }
+#endif
+  }
+}
+
 static CURLcode
 singleipconnect(struct connectdata *conn,
                 const Curl_addrinfo *ai, /* start connecting to this */
@@ -876,6 +905,9 @@ singleipconnect(struct connectdata *conn,
 
   Curl_sndbufset(sockfd);
 
+  if(data->set.tcp_keepalive)
+    tcpkeepalive(data, sockfd);
+
   if(data->set.fsockopt) {
     /* activate callback for setting socket options */
     error = data->set.fsockopt(data->set.sockopt_client,
index 395055f..c89234d 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -748,6 +748,13 @@ CURLcode Curl_init_userdefined(struct UserDefined *set)
   set->chunk_bgn      = ZERO_NULL;
   set->chunk_end      = ZERO_NULL;
 
+  /* tcp keepalives are disabled by default, but provide reasonable values for
+   * the interval and idle times.
+   */
+  set->tcp_keepalive = 0;
+  set->tcp_keepintvl = 60;
+  set->tcp_keepidle = 60;
+
   return res;
 }
 
@@ -811,6 +818,7 @@ CURLcode Curl_open(struct SessionHandle **curl)
        multi stack. */
   }
 
+
   if(res) {
     Curl_resolver_cleanup(data->state.resolver);
     if(data->state.headerbuff)
@@ -2545,6 +2553,16 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     result = Curl_set_dns_servers(data, va_arg(param, char *));
     break;
 
+  case CURLOPT_TCP_KEEPALIVE:
+    data->set.tcp_keepalive = (0 != va_arg(param, long))?TRUE:FALSE;
+    break;
+  case CURLOPT_TCP_KEEPIDLE:
+    data->set.tcp_keepidle = va_arg(param, long);
+    break;
+  case CURLOPT_TCP_KEEPINTVL:
+    data->set.tcp_keepintvl = va_arg(param, long);
+    break;
+
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
index adabf5b..a959bc7 100644 (file)
@@ -1539,6 +1539,10 @@ struct UserDefined {
 
   long gssapi_delegation; /* GSSAPI credential delegation, see the
                              documentation of CURLOPT_GSSAPI_DELEGATION */
+
+  bool tcp_keepalive;    /* use TCP keepalives */
+  long tcp_keepidle;     /* seconds in idle before sending keepalive probe */
+  long tcp_keepintvl;    /* seconds between TCP keepalive probes */
 };
 
 struct Names {