diff --git a/lib/core-net/close.c b/lib/core-net/close.c index 16128036b..0b02a3cdd 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -683,6 +683,7 @@ __lws_close_free_wsi_final(struct lws *wsi) #ifdef LWS_WITH_CGI if (wsi->http.cgi) { lws_spawn_piped_destroy(&wsi->http.cgi->lsp); + lws_sul_cancel(&wsi->http.cgi->sul_grace); lws_free_set_NULL(wsi->http.cgi); } #endif diff --git a/lib/core-net/dummy-callback.c b/lib/core-net/dummy-callback.c index 449758327..279fa022a 100644 --- a/lib/core-net/dummy-callback.c +++ b/lib/core-net/dummy-callback.c @@ -337,12 +337,16 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, else wsi->reason_bf &= ~LWS_CB_REASON_AUX_BF__CGI; - if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) + if (wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) { + lwsl_notice("%s: txn over\n", __func__); return -1; + } + break; } - if (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END) { + if ((wsi->http.cgi && wsi->http.cgi->cgi_transaction_over) || + (wsi->reason_bf & LWS_CB_REASON_AUX_BF__CGI_CHUNK_END)) { if (!wsi->mux_substream) { memcpy(buf + LWS_PRE, "0\x0d\x0a\x0d\x0a", 5); lwsl_debug("writing chunk term and exiting\n"); @@ -791,6 +795,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, if (wsi->http.cgi->post_in_expected && args->stdwsi[LWS_STDIN] && args->stdwsi[LWS_STDIN]->desc.filefd > 0) { wsi->http.cgi->post_in_expected -= n; + if (!wsi->http.cgi->post_in_expected) { struct lws *siwsi = args->stdwsi[LWS_STDIN]; @@ -803,8 +808,9 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, */ lwsl_notice("%s: expected POST in end: " - "closing stdin wsi %p, fd %d\n", - __func__, siwsi, siwsi->desc.sockfd); + "closing stdin wsi %p, fd %d\n", + __func__, siwsi, + siwsi->desc.sockfd); /* * We don't want the child / parent relationship @@ -815,6 +821,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, lws_remove_child_from_any_parent(siwsi); lws_wsi_close(siwsi, LWS_TO_KILL_ASYNC); wsi->http.cgi->lsp->stdwsi[LWS_STDIN] = NULL; + lws_spawn_stdwsi_closed(wsi->http.cgi->lsp, siwsi); } } diff --git a/lib/roles/cgi/cgi-server.c b/lib/roles/cgi/cgi-server.c index f9f954717..01eaf0d65 100644 --- a/lib/roles/cgi/cgi-server.c +++ b/lib/roles/cgi/cgi-server.c @@ -68,6 +68,43 @@ urlencode(const char *in, int inlen, char *out, int outlen) return out - start; } +static void +lws_cgi_grace(lws_sorted_usec_list_t *sul) +{ + struct lws_cgi *cgi = lws_container_of(sul, struct lws_cgi, sul_grace); + + /* act on the reap cb from earlier */ + + lwsl_notice("%s: wsi %p\n", __func__, cgi->wsi); + + if (!cgi->wsi->http.cgi->post_in_expected) + cgi->wsi->http.cgi->cgi_transaction_over = 1; + + lws_callback_on_writable(cgi->wsi); +} + + +static void +lws_cgi_reap_cb(void *opaque, lws_usec_t *accounting, siginfo_t *si, + int we_killed_him) +{ + struct lws *wsi = (struct lws *)opaque; + + /* + * The cgi has come to an end, by itself or with a signal... + */ + + lwsl_notice("%s: wsi %p post_in_expected %d\n", __func__, wsi, + (int)wsi->http.cgi->post_in_expected); + + /* + * Grace period to handle the incoming stdout + */ + + lws_sul_schedule(wsi->context, wsi->tsi, &wsi->http.cgi->sul_grace, + lws_cgi_grace, 1 * LWS_US_PER_SEC); +} + int lws_cgi(struct lws *wsi, const char * const *exec_array, int script_uri_path_len, int timeout_secs, @@ -357,6 +394,8 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, info.vh = wsi->vhost; info.ops = &role_ops_cgi; info.plsp = &wsi->http.cgi->lsp; + info.opaque = wsi; + info.reap_cb = lws_cgi_reap_cb; /* * Actually having made the env, as a cgi we don't need the ah @@ -386,6 +425,7 @@ lws_cgi(struct lws *wsi, const char * const *exec_array, return 0; bail: + lws_sul_cancel(&wsi->http.cgi->sul_grace); lws_free_set_NULL(wsi->http.cgi); lwsl_err("%s: failed\n", __func__); @@ -561,7 +601,7 @@ post_hpack_recode: cmd = LWS_WRITE_HTTP_HEADERS_CONTINUATION; if (wsi->http.cgi->headers_dumped + n != - wsi->http.cgi->headers_pos) { + wsi->http.cgi->headers_pos) { lwsl_notice("adding no fin flag\n"); cmd |= LWS_WRITE_NO_FIN; } @@ -578,14 +618,21 @@ post_hpack_recode: wsi->http.cgi->headers_pos) { wsi->hdr_state = LHCS_PAYLOAD; lws_free_set_NULL(wsi->http.cgi->headers_buf); - lwsl_debug("freed cgi headers\n"); + lwsl_debug("%s: freed cgi headers\n", __func__); + + if (wsi->http.cgi->post_in_expected) { + lwsl_notice("%s: post data still expected, asking for writeable\n", __func__); + lws_callback_on_writable(wsi); + } + } else { wsi->reason_bf |= LWS_CB_REASON_AUX_BF__CGI_HEADERS; lws_callback_on_writable(wsi); } - /* writeability becomes uncertain now we wrote + /* + * writeability becomes uncertain now we wrote * something, we must return to the event loop */ return 0; @@ -962,7 +1009,7 @@ lws_cgi_kill_terminated(struct lws_context_per_thread *pt) cgi = *pcgi; pcgi = &(*pcgi)->cgi_list; - if (cgi->lsp->child_pid <= 0) + if (!cgi || !cgi->lsp || cgi->lsp->child_pid <= 0) continue; /* we deferred killing him after reaping his PID */ @@ -1040,7 +1087,7 @@ lws_cgi_remove_and_kill(struct lws *wsi) pcgi = &(*pcgi)->cgi_list; } if (wsi->http.cgi->headers_buf) { - lwsl_debug("close: freed cgi headers\n"); + lwsl_debug("%s: close: freed cgi headers\n", __func__); lws_free_set_NULL(wsi->http.cgi->headers_buf); } /* we have a cgi going, we must kill it */ diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c index 97794197f..a67a509d2 100644 --- a/lib/roles/cgi/ops-cgi.c +++ b/lib/roles/cgi/ops-cgi.c @@ -46,6 +46,27 @@ rops_handle_POLLIN_cgi(struct lws_context_per_thread *pt, struct lws *wsi, return LWS_HPI_RET_WSI_ALREADY_DIED; } + if (!wsi->parent) { + lwsl_notice("%s: stdwsi content with parent\n", + __func__); + + return LWS_HPI_RET_HANDLED; + } + + if (!wsi->parent->http.cgi) { + lwsl_notice("%s: stdwsi content with deleted cgi object\n", + __func__); + + return LWS_HPI_RET_HANDLED; + } + + if (!wsi->parent->http.cgi->lsp) { + lwsl_notice("%s: stdwsi content with reaped lsp\n", + __func__); + + return LWS_HPI_RET_HANDLED; + } + args.ch = wsi->lsp_channel; args.stdwsi = &wsi->parent->http.cgi->lsp->stdwsi[0]; args.hdr_state = wsi->hdr_state; diff --git a/lib/roles/cgi/private-lib-roles-cgi.h b/lib/roles/cgi/private-lib-roles-cgi.h index 027ad1e34..2ca770510 100644 --- a/lib/roles/cgi/private-lib-roles-cgi.h +++ b/lib/roles/cgi/private-lib-roles-cgi.h @@ -55,7 +55,8 @@ struct lws; struct lws_cgi { struct lws_cgi *cgi_list; - struct lws_spawn_piped *lsp; + struct lws_spawn_piped *lsp; + lws_sorted_usec_list_t sul_grace; struct lws *wsi; /* owner */ unsigned char *headers_buf; diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 709f1c394..fab5479e7 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -2345,6 +2345,7 @@ lws_http_transaction_completed(struct lws *wsi) wsi->http.cgi_transaction_complete = 1; lws_cgi_remove_and_kill(wsi); lws_spawn_piped_destroy(&wsi->http.cgi->lsp); + lws_sul_cancel(&wsi->http.cgi->sul_grace); lws_free_set_NULL(wsi->http.cgi); wsi->http.cgi_transaction_complete = 0; diff --git a/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh b/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh index e705f84e8..5a96c37c8 100755 --- a/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh +++ b/minimal-examples/http-server/minimal-http-server-cgi/my-cgi-script.sh @@ -31,6 +31,6 @@ fi echo "
done" echo "" - +sleep 0.5 exit 0