close: make close notification go through writable
authorAndy Green <andy@warmcat.com>
Mon, 17 Jul 2017 02:11:17 +0000 (10:11 +0800)
committerAndy Green <andy@warmcat.com>
Wed, 19 Jul 2017 00:55:57 +0000 (08:55 +0800)
Until now we took the approach if just writing the close notification
broke something, we didn't care because we were closing the connection
anyway.

But with lws_meta, breaking stuff in the parent connection would be a
sticky problem outliving the closing child connection.

So this adds a new wsi state LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION
and makes the send go via the writable callback mechanism.

lib/client-parser.c
lib/handshake.c
lib/libwebsockets.c
lib/libwebsockets.h
lib/private-libwebsockets.h
lib/service.c

index b979c62..f99592e 100644 (file)
@@ -558,6 +558,7 @@ utf8_fail:                  lwsl_info("utf8 error\n");
                        lws_remove_wsi_from_draining_ext_list(wsi);
 
                if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
+                   wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
                    wsi->state == LWSS_AWAITING_CLOSE_ACK)
                        goto already_done;
 
index 8172e96..5e897e2 100644 (file)
@@ -229,6 +229,7 @@ postbody_completion:
 
        case LWSS_ESTABLISHED:
        case LWSS_AWAITING_CLOSE_ACK:
+       case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
        case LWSS_SHUTDOWN:
                if (lws_handshake_client(wsi, &buf, (size_t)len))
                        goto bail;
index 9cd34ee..51d8618 100755 (executable)
@@ -337,6 +337,7 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
                return;
 
        /* we tried the polite way... */
+       case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
        case LWSS_AWAITING_CLOSE_ACK:
                goto just_kill_connection;
 
@@ -453,29 +454,14 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 #if defined (LWS_WITH_ESP8266)
                wsi->close_is_pending_send_completion = 1;
 #endif
-               n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
-                             wsi->u.ws.close_in_ping_buffer_len,
-                             LWS_WRITE_CLOSE);
-               if (n >= 0) {
-                       /*
-                        * we have sent a nice protocol level indication we
-                        * now wish to close, we should not send anything more
-                        */
-                       wsi->state = LWSS_AWAITING_CLOSE_ACK;
-
-                       /*
-                        * ...and we should wait for a reply for a bit
-                        * out of politeness
-                        */
-                       lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1);
-                       lwsl_debug("sent close indication, awaiting ack\n");
 
-                       return;
-               }
-
-               lwsl_info("close: sending close packet failed, hanging up\n");
+               lwsl_debug("waiting for chance to send close\n");
+               wsi->waiting_to_send_close_frame = 1;
+               wsi->state = LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION;
+               lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 2);
+               lws_callback_on_writable(wsi);
 
-               /* else, the send failed and we should just hang up */
+               return;
        }
 
 just_kill_connection:
@@ -639,6 +625,7 @@ just_kill_connection:
            ((wsi->state_pre_close == LWSS_ESTABLISHED) ||
            (wsi->state_pre_close == LWSS_RETURNED_CLOSE_ALREADY) ||
            (wsi->state_pre_close == LWSS_AWAITING_CLOSE_ACK) ||
+           (wsi->state_pre_close == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) ||
            (wsi->state_pre_close == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) ||
            (wsi->mode == LWSCM_WS_CLIENT && wsi->state_pre_close == LWSS_HTTP) ||
            (wsi->mode == LWSCM_WS_SERVING && wsi->state_pre_close == LWSS_HTTP))) {
index ab0be75..fcd5d99 100644 (file)
@@ -3619,6 +3619,8 @@ enum pending_timeout {
        PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY            = 20,
        PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY               = 21,
        PENDING_TIMEOUT_KILLED_BY_SSL_INFO                      = 22,
+       PENDING_TIMEOUT_KILLED_BY_PARENT                        = 23,
+       PENDING_TIMEOUT_CLOSE_SEND                              = 24,
 
        /****** add new things just above ---^ ******/
 };
index 305145b..1c903d7 100644 (file)
@@ -488,6 +488,7 @@ enum lws_connection_states {
        LWSS_ESTABLISHED,
        LWSS_CLIENT_HTTP_ESTABLISHED,
        LWSS_CLIENT_UNCONNECTED,
+       LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION,
        LWSS_RETURNED_CLOSE_ALREADY,
        LWSS_AWAITING_CLOSE_ACK,
        LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE,
@@ -1648,6 +1649,7 @@ struct lws {
        unsigned int sending_chunked:1;
        unsigned int already_did_cce:1;
        unsigned int told_user_closed:1;
+       unsigned int waiting_to_send_close_frame:1;
        unsigned int ipv6:1;
 
 #if defined(LWS_WITH_ESP8266)
index 912b44c..65868e0 100644 (file)
@@ -58,13 +58,13 @@ lws_calllback_as_writeable(struct lws *wsi)
                n = LWS_CALLBACK_HTTP_WRITEABLE;
                break;
        }
-       lwsl_debug("%s: %p (user=%p)\n", __func__, wsi, wsi->user_space);
+
        return user_callback_handle_rxflow(wsi->protocol->callback,
                                           wsi, (enum lws_callback_reasons) n,
                                           wsi->user_space, NULL, 0);
 }
 
-int
+LWS_VISIBLE int
 lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 {
        int write_type = LWS_WRITE_PONG;
@@ -74,7 +74,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 #endif
        int ret, m, n;
 
-       //lwsl_err("%s: %p\n", __func__, wsi);
+//     lwsl_err("%s: %p\n", __func__, wsi);
 
        wsi->leave_pollout_active = 0;
        wsi->handling_pollout = 1;
@@ -144,7 +144,29 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 #endif
 
        /* Priority 3: pending control packets (pong or close)
+        *
+        * 3a: close notification packet requested from close api
         */
+
+       if (wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) {
+               lwsl_debug("sending close packet\n");
+               wsi->waiting_to_send_close_frame = 0;
+               n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[LWS_PRE],
+                             wsi->u.ws.close_in_ping_buffer_len,
+                             LWS_WRITE_CLOSE);
+               if (n >= 0) {
+                       wsi->state = LWSS_AWAITING_CLOSE_ACK;
+                       lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 1);
+                       lwsl_debug("sent close indication, awaiting ack\n");
+
+                       goto bail_ok;
+               }
+
+               goto bail_die;
+       }
+
+       /* else, the send failed and we should just hang up */
+
        if ((wsi->state == LWSS_ESTABLISHED &&
             wsi->u.ws.ping_pending_flag) ||
            (wsi->state == LWSS_RETURNED_CLOSE_ALREADY &&
@@ -319,7 +341,6 @@ user_service:
                wsi->handling_pollout = 0;
 
                /* cannot get leave_pollout_active set after the above */
-
                if (!eff && wsi->leave_pollout_active)
                        /* got set inbetween sampling eff and clearing
                         * handling_pollout, force POLLOUT on */
@@ -1039,11 +1060,15 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
 
                /* 1: something requested a callback when it was OK to write */
 
+               if (wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION)
+                       lwsl_notice("xxx\n");
+
                if ((pollfd->revents & LWS_POLLOUT) &&
                    ((wsi->state == LWSS_ESTABLISHED ||
                     wsi->state == LWSS_HTTP2_ESTABLISHED ||
                     wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS ||
                     wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
+                    wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
                     wsi->state == LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE)) &&
                    lws_handle_POLLOUT_event(wsi, pollfd)) {
                        if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
@@ -1053,6 +1078,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t
                }
 
                if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
+                   wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
                    wsi->state == LWSS_AWAITING_CLOSE_ACK) {
                        /*
                         * we stopped caring about anything except control