diff --git a/lib/client-parser.c b/lib/client-parser.c index b979c626..f99592e5 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 8172e965..5e897e24 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 9cd34eec..51d86181 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"); + 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); - return; - } - - lwsl_info("close: sending close packet failed, hanging up\n"); - - /* 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 ab0be757..fcd5d99b 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 305145bf..1c903d79 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 912b44cb..65868e05 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