From d78c93254b1f49359713af897abe3206fa7823a1 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Thu, 7 Sep 2017 12:55:12 +0800 Subject: [PATCH] cgi: stdout POLLHUP holy grail This a) directly discovers cgi stdout POLLUP and b) modulates rx flow control on CGI STDOUT according to the outgoing writeable service. When the outgoing writeable service finally sees 0 read() waiting for it even though it was signalled for POLLIN, it knows it is a POLLHUP. Critically when it sees POLLHUP like that, it leaves the rx flow control defeating any further stdout POLLIN signalling while the rest of the CGI lifecycle completes, eliminating busywaiting during the CGI. --- lib/context.c | 13 +++++++++++-- lib/libwebsockets.c | 13 +++++++++---- lib/private-libwebsockets.h | 1 + lib/service.c | 11 ++++++----- 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/lib/context.c b/lib/context.c index 7d76c803..e11af453 100644 --- a/lib/context.c +++ b/lib/context.c @@ -246,9 +246,14 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_HTTP_WRITEABLE: #ifdef LWS_WITH_CGI - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI) { - if (lws_cgi_write_split_stdout_headers(wsi) < 0) + if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS | LWS_CB_REASON_AUX_BF__CGI)) { + n = lws_cgi_write_split_stdout_headers(wsi); + if (n < 0) { + lwsl_debug("LWS_CB_REASON_AUX_BF__CGI forcing close\n"); return -1; + } + if (!n) + lws_rx_flow_control(wsi->cgi->stdwsi[LWS_STDOUT], 1); if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_HEADERS) wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI_HEADERS; @@ -258,6 +263,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, } if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) { + lwsl_debug("writing chunk terminator and exiting\n"); n = lws_write(wsi, (unsigned char *)"0\x0d\x0a\x0d\x0a", 5, LWS_WRITE_HTTP); /* always close after sending it */ @@ -363,6 +369,8 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, /* TBD stdin rx flow control */ break; case LWS_STDOUT: + /* quench POLLIN on STDOUT until MASTER got writeable */ + lws_rx_flow_control(args->stdwsi[LWS_STDOUT], 0); wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI; /* when writing to MASTER would not block */ lws_callback_on_writable(wsi); @@ -385,6 +393,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, wsi->cgi->explicitly_chunked, (uint64_t)wsi->cgi->content_length); if (!wsi->cgi->explicitly_chunked && !wsi->cgi->content_length) { /* send terminating chunk */ + lwsl_debug("LWS_CALLBACK_CGI_TERMINATED: looking to send terminating chunk\n"); wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_CHUNK_END; lws_callback_on_writable(wsi); lws_set_timeout(wsi, PENDING_TIMEOUT_CGI, 3); diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 32b27325..3ce469f0 100755 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -3028,8 +3028,13 @@ lws_cgi_write_split_stdout_headers(struct lws *wsi) return -1; } wsi->cgi->content_length_seen += m; + } else { + if (wsi->cgi_stdout_zero_length) { + lwsl_debug("%s: failed to read anything: stdout is POLLHUP'd\n", __func__); + return 1; + } + wsi->cgi_stdout_zero_length = 1; } - return 0; } @@ -3153,7 +3158,7 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt) if (!cgi->content_length) { /* - * well, if he sends chunked... give him 5s after the + * well, if he sends chunked... give him 2s after the * cgi terminated to send buffered */ cgi->chunked_grace++; @@ -3192,7 +3197,7 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt) /* we deferred killing him after reaping his PID */ if (cgi->chunked_grace) { cgi->chunked_grace++; - if (cgi->chunked_grace < 5) + if (cgi->chunked_grace < 2) continue; goto finish_him; } @@ -3214,7 +3219,7 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt) if (!cgi->content_length) { /* - * well, if he sends chunked... give him 5s after the + * well, if he sends chunked... give him 2s after the * cgi terminated to send buffered */ cgi->chunked_grace++; diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 5c75bed6..ee2edd36 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -1671,6 +1671,7 @@ struct lws { unsigned int ipv6:1; unsigned int parent_carries_io:1; unsigned int parent_pending_cb_on_writable:1; + unsigned int cgi_stdout_zero_length:1; #if defined(LWS_WITH_ESP8266) unsigned int pending_send_completion:3; diff --git a/lib/service.c b/lib/service.c index 1850426a..184ded08 100644 --- a/lib/service.c +++ b/lib/service.c @@ -902,7 +902,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t wsi, buf, pt->ah_pool[n].rxpos, pt->ah_pool[n].rxlen, pt->ah_pool[n].pos); - + buf[0] = '\0'; m = 0; do { c = lws_token_to_string(m); @@ -915,11 +915,12 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, int t continue; } - lws_hdr_copy(wsi, buf, sizeof buf, m); - buf[sizeof(buf) - 1] = '\0'; + if (lws_hdr_copy(wsi, buf, sizeof buf, m) > 0) { + buf[sizeof(buf) - 1] = '\0'; - lwsl_notice(" %s = %s\n", - (const char *)c, buf); + lwsl_notice(" %s = %s\n", + (const char *)c, buf); + } m++; } while (1);