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.
This commit is contained in:
Andy Green 2017-07-17 10:11:17 +08:00
parent faa1526b39
commit 3b0066cb3f
6 changed files with 44 additions and 25 deletions

View 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;

View 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;

View file

@ -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))) {

View 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 ---^ ******/
};

View 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)

View 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