From 3b0066cb3fec6baee98a3dcb2cecf11463453ed5 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Mon, 17 Jul 2017 10:11:17 +0800 Subject: [PATCH] close: make close notification go through writable 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 | 1 + lib/handshake.c | 1 + lib/libwebsockets.c | 29 ++++++++--------------------- lib/libwebsockets.h | 2 ++ lib/private-libwebsockets.h | 2 ++ lib/service.c | 34 ++++++++++++++++++++++++++++++---- 6 files changed, 44 insertions(+), 25 deletions(-) diff --git a/lib/client-parser.c b/lib/client-parser.c index b979c62..f99592e 100644 --- a/lib/client-parser.c +++ b/lib/client-parser.c @@ -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; diff --git a/lib/handshake.c b/lib/handshake.c index 8172e96..5e897e2 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -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; diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 9cd34ee..51d8618 100755 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -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))) { diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index ab0be75..fcd5d99 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -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 ---^ ******/ }; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 305145b..1c903d7 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -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) diff --git a/lib/service.c b/lib/service.c index 912b44c..65868e0 100644 --- a/lib/service.c +++ b/lib/service.c @@ -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 -- 2.7.4