introduce keepalive option and make common socket options function
authorAndy Green <andy.green@linaro.org>
Sat, 9 Feb 2013 04:25:31 +0000 (12:25 +0800)
committerAndy Green <andy.green@linaro.org>
Sat, 9 Feb 2013 06:27:38 +0000 (14:27 +0800)
Signed-off-by: Andy Green <andy.green@linaro.org>
README.coding
changelog
lib/client-handshake.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/private-libwebsockets.h
lib/server.c
libwebsockets-api-doc.html

index b994f3a..7c9d631 100644 (file)
@@ -162,3 +162,25 @@ copy interesting headers by handling LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION
 callback, for clients there's a new callback just for this purpose
 LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH.
 
+
+TCP Keepalive
+-------------
+
+It is possible for a connection which is not being used to send to die
+silently somewhere between the peer and the side not sending.  In this case
+by default TCP will just not report anything and you will never get any more
+incoming data or sign the link is dead until you try to send.
+
+To deal with getting a notification of that situation, you can choose to
+enable TCP keepalives on all libwebsockets sockets, when you create the
+context.
+
+To enable keepalive, set the ka_time member of the context creation parameter
+struct to a nonzero value (in seconds) at context creation time.  You should
+also fill ka_probes and ka_interval in that case.
+
+With keepalive enabled, the TCP layer will send control packets that should
+stimulate a response from the peer without affecting link traffic.  If the
+response is not coming, the socket will announce an error at poll() forcing
+a close.
+
index 35f5125..fcf091d 100644 (file)
--- a/changelog
+++ b/changelog
@@ -10,6 +10,14 @@ User api additions
         "1.1 9e7f737", representing the library version from configure.ac
         and the git HEAD hash the library was built from
 
+ - TCP Keepalive can now optionally be applied to all lws sockets, with
+       controllable timeout, number of probes and probe interval.  This
+       enables detection of idle connections which are logically okay, but
+       are in fact dead, due to network connectivity issues at the server,
+       client, or any intermediary.  By default it's not enabled, but you
+       can enable it by setting a non-zero timeout (in seconds) at the new
+       ka_time member at context creation time.
+
 
 User api changes
 ----------------
index f3113b0..48c9a96 100644 (file)
@@ -10,10 +10,6 @@ struct libwebsocket *__libwebsocket_client_connect_2(
        int n;
        int plen = 0;
        char pkt[512];
-       int opt = 1;
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
-       struct protoent *tcp_proto;
-#endif
 
        lwsl_client("__libwebsocket_client_connect_2\n");
 #ifndef LWS_NO_EXTENSIONS
@@ -41,7 +37,8 @@ struct libwebsocket *__libwebsocket_client_connect_2(
         * prepare the actual connection (to the proxy, if any)
         */
 
-       lwsl_client("__libwebsocket_client_connect_2: address %s", wsi->c_address);
+       lwsl_client("__libwebsocket_client_connect_2: address %s\n",
+                                                               wsi->c_address);
 
        server_hostent = gethostbyname(wsi->c_address);
        if (server_hostent == NULL) {
@@ -61,16 +58,6 @@ struct libwebsocket *__libwebsocket_client_connect_2(
        server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr);
        bzero(&server_addr.sin_zero, 8);
 
-       /* Disable Nagle */
-#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
-       setsockopt(wsi->sock, SOL_TCP, TCP_NODELAY,
-                                             (const void *)&opt, sizeof(opt));
-#else
-       tcp_proto = getprotobyname("TCP");
-       setsockopt(wsi->sock, tcp_proto->p_proto, TCP_NODELAY,
-                                                           &opt, sizeof(opt));
-#endif
-
        if (connect(wsi->sock, (struct sockaddr *)&server_addr,
                                             sizeof(struct sockaddr)) == -1)  {
                lwsl_debug("Connect failed\n");
@@ -80,6 +67,12 @@ struct libwebsocket *__libwebsocket_client_connect_2(
 
        lwsl_client("connected\n");
 
+       if (lws_set_socket_options(context, wsi->sock)) {
+               lwsl_err("Failed to set wsi socket options\n");
+               close(wsi->sock);
+               goto oom4;
+       }
+
        insert_wsi_socket_into_fds(context, wsi);
 
        /* we are connected to server, or proxy */
index 31d6f67..55fcf00 100644 (file)
@@ -519,6 +519,60 @@ int libwebsockets_get_random(struct libwebsocket_context *context,
        return n;
 }
 
+int lws_set_socket_options(struct libwebsocket_context *context, int fd)
+{
+       int optval = 1;
+       socklen_t optlen = sizeof(optval);
+#ifdef WIN32
+       unsigned long optl = 0;
+#endif
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
+       struct protoent *tcp_proto;
+#endif
+
+       if (context->ka_time) {
+               /* enable keepalive on this socket */
+               optval = 1;
+               if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
+                                            (const void *)&optval, optlen) < 0)
+                       return 1;
+
+               /* set the keepalive conditions we want on it too */
+               optval = context->ka_time;
+               if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE,
+                                            (const void *)&optval, optlen) < 0)
+                       return 1;
+
+               optval = context->ka_probes;
+               if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL,
+                                            (const void *)&optval, optlen) < 0)
+                       return 1;
+
+               optval = context->ka_interval;
+               if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT,
+                                            (const void *)&optval, optlen) < 0)
+                       return 1;
+       }
+
+       /* Disable Nagle */
+       optval = 1;
+#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
+       setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
+#else
+       tcp_proto = getprotobyname("TCP");
+       setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
+#endif
+
+       /* We are nonblocking... */
+#ifdef WIN32
+       ioctlsocket(fd, FIONBIO, &optl);
+#else
+       fcntl(fd, F_SETFL, O_NONBLOCK);
+#endif
+
+       return 0;
+}
+
 int lws_send_pipe_choked(struct libwebsocket *wsi)
 {
        struct pollfd fds;
index 210ecd4..5e2681c 100644 (file)
@@ -742,6 +742,13 @@ struct libwebsocket_extension {
  * @options:   0, or LWS_SERVER_OPTION_DEFEAT_CLIENT_MASK
  * @user:      optional user pointer that can be recovered via the context
  *             pointer using libwebsocket_context_user
+ * @ka_time:   0 for no keepalive, otherwise apply this keepalive timeout to
+ *             all libwebsocket sockets, client or server
+ * @ka_probes: if ka_time was nonzero, after the timeout expires how many
+ *             times to try to get a response from the peer before giving up
+ *             and killing the connection
+ * @ka_interval: if ka_time was nonzero, how long to wait before each ka_probes
+ *             attempt
  */
 
 struct lws_context_creation_info {
@@ -756,6 +763,10 @@ struct lws_context_creation_info {
        int uid;
        unsigned int options;
        void *user;
+       int ka_time;
+       int ka_probes;
+       int ka_interval;
+
 };
 
 LWS_EXTERN
index a53d00f..267b5cc 100644 (file)
@@ -265,6 +265,10 @@ struct libwebsocket_context {
        int listen_service_fd;
        int listen_service_extraseen;
 
+       int ka_time;
+       int ka_probes;
+       int ka_interval;
+
 #ifdef LWS_LATENCY
        unsigned long worst_latency;
        char worst_latency_info[256];
@@ -489,6 +493,9 @@ user_callback_handle_rxflow(callback_function, struct libwebsocket_context * con
                         enum libwebsocket_callback_reasons reason, void *user,
                                                          void *in, size_t len);
 
+extern int
+lws_set_socket_options(struct libwebsocket_context *context, int fd);
+
 #ifndef LWS_OPENSSL_SUPPORT
 
 unsigned char *
index 3bb28a2..aa51b12 100644 (file)
@@ -133,7 +133,6 @@ int lws_server_socket_service(struct libwebsocket_context *context,
        unsigned int clilen;
        struct sockaddr_in cli_addr;
        int n;
-       int opt = 1;
        ssize_t len;
 #ifdef LWS_OPENSSL_SUPPORT
        int m;
@@ -217,18 +216,7 @@ int lws_server_socket_service(struct libwebsocket_context *context,
                        break;
                }
 
-               /* Disable Nagle */
-               opt = 1;
-               setsockopt(accept_fd, IPPROTO_TCP, TCP_NODELAY,
-                                             (const void *)&opt, sizeof(opt));
-
-               /* We are nonblocking... */
-               #ifdef WIN32
-               opt = 0;
-               ioctlsocket(accept_fd, FIONBIO, (unsigned long *)&opt );
-               #else
-               fcntl(accept_fd, F_SETFL, O_NONBLOCK);
-               #endif
+               lws_set_socket_options(context, accept_fd);
 
                /*
                 * look at who we connected to and give user code a chance
index 65a3da7..a3f6415 100644 (file)
@@ -968,6 +968,9 @@ all sessions, etc, if it wants
 &nbsp; &nbsp; <i>int</i> <b>uid</b>;<br>
 &nbsp; &nbsp; <i>unsigned int</i> <b>options</b>;<br>
 &nbsp; &nbsp; <i>void *</i> <b>user</b>;<br>
+&nbsp; &nbsp; <i>int</i> <b>ka_time</b>;<br>
+&nbsp; &nbsp; <i>int</i> <b>ka_probes</b>;<br>
+&nbsp; &nbsp; <i>int</i> <b>ka_interval</b>;<br>
 };<br>
 <h3>Members</h3>
 <dl>
@@ -1005,5 +1008,15 @@ else ignored
 <dt><b>user</b>
 <dd>optional user pointer that can be recovered via the context
 pointer using libwebsocket_context_user
+<dt><b>ka_time</b>
+<dd>0 for no keepalive, otherwise apply this keepalive timeout to
+all libwebsocket sockets, client or server
+<dt><b>ka_probes</b>
+<dd>if ka_time was nonzero, after the timeout expires how many
+times to try to get a response from the peer before giving up
+and killing the connection
+<dt><b>ka_interval</b>
+<dd>if ka_time was nonzero, how long to wait before each ka_probes
+attempt
 </dl>
 <hr>