introduce keepalive option and make common socket options function

Signed-off-by: Andy Green <andy.green@linaro.org>
diff --git a/README.coding b/README.coding
index b994f3a..7c9d631 100644
--- a/README.coding
+++ b/README.coding
@@ -162,3 +162,25 @@
 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.
+
diff --git a/changelog b/changelog
index 35f5125..fcf091d 100644
--- a/changelog
+++ b/changelog
@@ -10,6 +10,14 @@
  	 "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
 ----------------
diff --git a/lib/client-handshake.c b/lib/client-handshake.c
index f3113b0..48c9a96 100644
--- a/lib/client-handshake.c
+++ b/lib/client-handshake.c
@@ -10,10 +10,6 @@
 	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 @@
 	 * 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 @@
 	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 @@
 
 	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 */
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 31d6f67..55fcf00 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -519,6 +519,60 @@
 	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;
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 210ecd4..5e2681c 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -742,6 +742,13 @@
  * @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 @@
 	int uid;
 	unsigned int options;
 	void *user;
+	int ka_time;
+	int ka_probes;
+	int ka_interval;
+
 };
 
 LWS_EXTERN
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index a53d00f..267b5cc 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -265,6 +265,10 @@
 	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 @@
 			 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 *
diff --git a/lib/server.c b/lib/server.c
index 3bb28a2..aa51b12 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -133,7 +133,6 @@
 	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 @@
 			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
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index 65a3da7..a3f6415 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -968,6 +968,9 @@
 &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 @@
 <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>