add extra state for waiting on close ack with timeout

Signed-off-by: Andy Green <andy@warmcat.com>
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index c6aca20..fdfaa46 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -158,7 +158,49 @@
 	if (old_state == WSI_STATE_DEAD_SOCKET)
 		return;
 
-	/* remove this fd from wsi mapping hashtable */
+	wsi->close_reason = reason;
+
+	/*
+	 * signal we are closing, libsocket_write will
+	 * add any necessary version-specific stuff.  If the write fails,
+	 * no worries we are closing anyway.  If we didn't initiate this
+	 * close, then our state has been changed to
+	 * WSI_STATE_RETURNED_CLOSE_ALREADY and we will skip this.
+	 *
+	 * Likewise if it's a second call to close this connection after we
+	 * sent the close indication to the peer already, we are in state
+	 * WSI_STATE_AWAITING_CLOSE_ACK and will skip doing this a second time.
+	 */
+
+	if (old_state == WSI_STATE_ESTABLISHED &&
+					  reason != LWS_CLOSE_STATUS_NOSTATUS) {
+		n = libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING],
+							    0, LWS_WRITE_CLOSE);
+		if (!n) {
+			/*
+			 * we have sent a nice protocol level indication we
+			 * now wish to close, we should not send anything more
+			 */
+
+			wsi->state = WSI_STATE_AWAITING_CLOSE_ACK;
+
+			/* and we should wait for a reply for a bit */
+
+			libwebsocket_set_timeout(wsi,
+						  PENDING_TIMEOUT_CLOSE_ACK, 5);
+
+			fprintf(stderr, "sent close indication, awaiting ack\n");
+
+			return;
+		}
+
+		/* else, the send failed and we should just hang up */
+	}
+
+	/*
+	 * we won't be servicing or receiving anything further from this guy
+	 * remove this fd from wsi mapping hashtable
+	 */
 
 	delete_from_fd(context, wsi->sock);
 
@@ -181,20 +223,6 @@
 	context->protocols[0].callback(context, wsi,
 		    LWS_CALLBACK_DEL_POLL_FD, (void *)(long)wsi->sock, NULL, 0);
 
-	wsi->close_reason = reason;
-
-	/*
-	 * signal we are closing, libsocket_write will
-	 * add any necessary version-specific stuff.  If the write fails,
-	 * no worries we are closing anyway.  If we didn't initiate this
-	 * close, then our state has been changed to
-	 * WSI_STATE_RETURNED_CLOSE_ALREADY and we will skip this
-	 */
-
-	if (old_state == WSI_STATE_ESTABLISHED)
-		libwebsocket_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 0,
-							       LWS_WRITE_CLOSE);
-
 	wsi->state = WSI_STATE_DEAD_SOCKET;
 
 	/* tell the user it's all over for this guy */
@@ -586,9 +614,11 @@
 			 * connection
 			 */
 
-			if (tv.tv_sec > wsi->pending_timeout_limit)
+			if (tv.tv_sec > wsi->pending_timeout_limit) {
+				fprintf(stderr, "TIMEDOUT WAITING\n");
 				libwebsocket_close_and_free_session(context,
 						wsi, LWS_CLOSE_STATUS_NOSTATUS);
+			}
 		}
 	}
 
@@ -1607,10 +1637,12 @@
 
 		/* the guy requested a callback when it was OK to write */
 
-		if (pollfd->revents & POLLOUT)
-			if (lws_handle_POLLOUT_event(context, wsi, pollfd) < 0) {
-				libwebsocket_close_and_free_session(context, wsi,
-						       LWS_CLOSE_STATUS_NORMAL);
+		if ((pollfd->revents & POLLOUT) &&
+					    wsi->state == WSI_STATE_ESTABLISHED)
+			if (lws_handle_POLLOUT_event(context, wsi,
+								  pollfd) < 0) {
+				libwebsocket_close_and_free_session(
+					 context, wsi, LWS_CLOSE_STATUS_NORMAL);
 				return 1;
 			}