add extra state for waiting on close ack with timeout
authorAndy Green <andy@warmcat.com>
Mon, 7 Mar 2011 07:08:12 +0000 (07:08 +0000)
committerAndy Green <andy@warmcat.com>
Mon, 7 Mar 2011 07:08:12 +0000 (07:08 +0000)
Signed-off-by: Andy Green <andy@warmcat.com>
lib/handshake.c
lib/libwebsockets.c
lib/parsers.c
lib/private-libwebsockets.h

index 6a07d51..7c8310f 100644 (file)
@@ -686,6 +686,7 @@ libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi
 
                break;
 
+       case WSI_STATE_AWAITING_CLOSE_ACK:
        case WSI_STATE_ESTABLISHED:
                switch (wsi->mode) {
                case LWS_CONNMODE_WS_CLIENT:
index c6aca20..fdfaa46 100644 (file)
@@ -158,7 +158,49 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
        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 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context,
        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 @@ libwebsocket_service_fd(struct libwebsocket_context *context,
                         * 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 @@ bail2:
 
                /* 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;
                        }
 
index fd26cb7..b116ddd 100644 (file)
@@ -617,6 +617,16 @@ spill:
 
                switch (wsi->opcode) {
                case LWS_WS_OPCODE_04__CLOSE:
+                       /* is this an acknowledgement of our close? */
+                       if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
+                               /*
+                                * fine he has told us he is closing too, let's
+                                * finish our close
+                                */
+                               fprintf(stderr, "seen client close ack\n");
+                               return -1;
+                       }
+                       fprintf(stderr, "server sees client close packet\n");
                        /* parrot the close packet payload back */
                        n = libwebsocket_write(wsi, (unsigned char *)
                           &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
@@ -940,10 +950,21 @@ spill:
 
                switch (wsi->opcode) {
                case LWS_WS_OPCODE_04__CLOSE:
+                       /* is this an acknowledgement of our close? */
+                       if (wsi->state == WSI_STATE_AWAITING_CLOSE_ACK) {
+                               /*
+                                * fine he has told us he is closing too, let's
+                                * finish our close
+                                */
+                               fprintf(stderr, "seen server's close ack\n");
+                               return -1;
+                       }
+                       fprintf(stderr, "client sees server close packet len = %d\n", wsi->rx_user_buffer_head);
                        /* parrot the close packet payload back */
                        n = libwebsocket_write(wsi, (unsigned char *)
                           &wsi->rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING],
                                     wsi->rx_user_buffer_head, LWS_WRITE_CLOSE);
+                       fprintf(stderr, "client writing close ack returned %d\n", n);
                        wsi->state = WSI_STATE_RETURNED_CLOSE_ALREADY;
                        /* close the connection */
                        return -1;
index fa61f49..e93b0f0 100644 (file)
@@ -129,6 +129,7 @@ enum lws_connection_states {
        WSI_STATE_ESTABLISHED,
        WSI_STATE_CLIENT_UNCONNECTED,
        WSI_STATE_RETURNED_CLOSE_ALREADY,
+       WSI_STATE_AWAITING_CLOSE_ACK
 };
 
 enum lws_rx_parse_state {
@@ -214,6 +215,7 @@ enum pending_timeout {
        PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
        PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
        PENDING_TIMEOUT_AWAITING_PING,
+       PENDING_TIMEOUT_CLOSE_ACK,
 };