diff --git a/lib/core-net/close.c b/lib/core-net/close.c index f58969eb6..25de2d015 100644 --- a/lib/core-net/close.c +++ b/lib/core-net/close.c @@ -326,6 +326,11 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, just_kill_connection: +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->http.buflist_post_body) + lws_buflist_destroy_all_segments(&wsi->http.buflist_post_body); +#endif + if (wsi->role_ops->close_kill_connection) wsi->role_ops->close_kill_connection(wsi, reason); diff --git a/lib/core-net/dummy-callback.c b/lib/core-net/dummy-callback.c index 94b28b160..3f6666b44 100644 --- a/lib/core-net/dummy-callback.c +++ b/lib/core-net/dummy-callback.c @@ -250,13 +250,29 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, break; #if !defined(LWS_NO_SERVER) case LWS_CALLBACK_HTTP_BODY_COMPLETION: +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->child_list) + lwsl_user("%s: LWS_CALLBACK_HTTP_BODY_COMPLETION: %d\n", __func__, (int)len); + break; +#endif case LWS_CALLBACK_HTTP_FILE_COMPLETION: if (lws_http_transaction_completed(wsi)) return -1; break; #endif +#if defined(LWS_WITH_HTTP_PROXY) + case LWS_CALLBACK_HTTP_BODY: + if (wsi->child_list) { + lwsl_user("%s: LWS_CALLBACK_HTTP_BODY: stashing %d\n", __func__, (int)len); + lws_buflist_append_segment(&wsi->http.buflist_post_body, in, len); + lws_callback_on_writable(wsi->child_list); + } + break; +#endif + case LWS_CALLBACK_HTTP_WRITEABLE: + // lwsl_err("%s: LWS_CALLBACK_HTTP_WRITEABLE\n", __func__); #ifdef LWS_WITH_CGI if (wsi->reason_bf & (LWS_CB_REASON_AUX_BF__CGI_HEADERS | LWS_CB_REASON_AUX_BF__CGI)) { @@ -317,6 +333,7 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, __func__); return -1; } + lws_callback_on_writable(wsi); break; } @@ -494,6 +511,13 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, lwsl_debug("%s: LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: " "prepared headers\n", __func__); + + /* + * so at this point, the onward client connection can bear + * traffic. We might be doing a POST and have pending cached + * inbound stuff to send, it can go now. + */ + lws_callback_on_writable(parent); break; } @@ -518,7 +542,6 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: parent = lws_get_parent(wsi); - if (!parent) break; @@ -550,7 +573,6 @@ lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, return -1; break; - #endif #ifdef LWS_WITH_CGI diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index c06b92c0f..6ae195f37 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -654,8 +654,51 @@ rops_handle_POLLIN_h1(struct lws_context_per_thread *pt, struct lws *wsi, static int rops_handle_POLLOUT_h1(struct lws *wsi) { - if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY) + + if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY) { +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->http.proxy_clientside) { + unsigned char *buf; + size_t len = lws_buflist_next_segment_len( + &wsi->parent->http.buflist_post_body, &buf); + int n; + + lwsl_debug("%s: %p: proxying body %d %d %d %d %d\n", + __func__, wsi, (int)len, + (int)wsi->http.tx_content_length, + (int)wsi->http.tx_content_remain, + (int)wsi->http.rx_content_length, + (int)wsi->http.rx_content_remain + ); + + n = lws_write(wsi, buf, len, LWS_WRITE_HTTP); + if (n < 0) { + lwsl_err("%s: PROXY_BODY: write failed\n", + __func__); + return -1; + } + + lws_buflist_use_segment(&wsi->parent->http.buflist_post_body, len); + + if (wsi->parent->http.buflist_post_body) + lws_callback_on_writable(wsi); + else { +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + /* prepare ourselves to do the parsing */ + wsi->http.ah->parser_state = WSI_TOKEN_NAME_PART; + wsi->http.ah->lextable_pos = 0; +#if defined(LWS_WITH_CUSTOM_HEADERS) + wsi->http.ah->unk_pos = 0; +#endif +#endif + lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY); + lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, + wsi->context->timeout_secs); + } + } +#endif return LWS_HP_RET_USER_SERVICE; + } if (lwsi_role_client(wsi)) return LWS_HP_RET_USER_SERVICE; diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index b4b48b7e6..c72f0b479 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -979,6 +979,9 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) lwsl_info(" h2 action start...\n"); n = lws_http_action(w); + if (n < 0) + lwsl_info (" h2 action result %d\n", n); + else lwsl_info(" h2 action result %d " "(wsi->http.rx_content_remain %lld)\n", n, w->http.rx_content_remain); @@ -989,7 +992,7 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) * states. In those cases we will hear about * END_STREAM going out in the POLLOUT handler. */ - if (!w->h2.pending_status_body && + if (n >= 0 && !w->h2.pending_status_body && (n || w->h2.send_END_STREAM)) { lwsl_info("closing stream after h2 action\n"); lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, @@ -997,6 +1000,9 @@ rops_perform_user_POLLOUT_h2(struct lws *wsi) wa = &wsi->h2.child_list; } + if (n < 0) + wa = &wsi->h2.child_list; + goto next_child; } diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index b0574e630..4ad69ccbb 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -403,10 +403,15 @@ start_ws_handshake: } if (wsi->client_http_body_pending) { + lwsl_debug("body pending\n"); lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY); lws_set_timeout(wsi, PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, context->timeout_secs); +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->http.proxy_clientside) + lws_callback_on_writable(wsi); +#endif /* user code must ask for writable callback */ break; } @@ -436,6 +441,12 @@ start_ws_handshake: goto client_http_body_sent; case LRS_ISSUE_HTTP_BODY: +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->http.proxy_clientside) { + lws_callback_on_writable(wsi); + break; + } +#endif if (wsi->client_http_body_pending) { //lws_set_timeout(wsi, // PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD, @@ -1083,6 +1094,22 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt) lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); } + +#if defined(LWS_WITH_HTTP_PROXY) + if (wsi->parent && + lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + p += snprintf(p, 128, "Content-Length: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH)); + if (atoi(lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_LENGTH))) + wsi->client_http_body_pending = 1; + } + if (wsi->parent && + lws_hdr_total_length(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)) { + p += snprintf(p, 128, "Content-Type: %s\x0d\x0a", + lws_hdr_simple_ptr(wsi->parent, WSI_TOKEN_HTTP_CONTENT_TYPE)); + } +#endif + #if defined(LWS_ROLE_WS) if (wsi->do_ws) { const char *conn1 = ""; diff --git a/lib/roles/http/private.h b/lib/roles/http/private.h index 891409ed4..9c275f9f7 100644 --- a/lib/roles/http/private.h +++ b/lib/roles/http/private.h @@ -215,6 +215,7 @@ struct _lws_http_mode_related { #if defined(LWS_WITH_HTTP_PROXY) struct lws_rewrite *rw; + struct lws_buflist *buflist_post_body; #endif struct allocated_headers *ah; struct lws *ah_wait_list; diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index fdc5cf00a..bf848ae89 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -746,6 +746,7 @@ lws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len) if (hm->origin_protocol == LWSMPRO_CALLBACK || ((hm->origin_protocol == LWSMPRO_CGI || lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) || + lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) || (wsi->http2_substream && lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH)) || @@ -1095,8 +1096,19 @@ lws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit, else i.host = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST); i.origin = NULL; - if (!ws) - i.method = "GET"; + if (!ws) { + if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI) +#if defined(LWS_WITH_HTTP2) + || ( + lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) && + !strcmp(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD), "post") + ) +#endif + ) + i.method = "POST"; + else + i.method = "GET"; + } lws_snprintf(host, sizeof(host), "%s:%d", i.address, i.port); i.host = host; @@ -1402,11 +1414,20 @@ lws_http_action(struct lws *wsi) * The mount is a reverse proxy? */ + // if (hit) // lwsl_notice("%s: origin_protocol: %d\n", __func__, hit->origin_protocol); + //else + // lwsl_notice("%s: no hit\n", __func__); if (hit->origin_protocol == LWSMPRO_HTTPS || - hit->origin_protocol == LWSMPRO_HTTP) - return lws_http_proxy_start(wsi, hit, uri_ptr, 0); + hit->origin_protocol == LWSMPRO_HTTP) { + n = lws_http_proxy_start(wsi, hit, uri_ptr, 0); + lwsl_notice("proxy start says %d\n", n); + if (n) + return n; + + goto deal_body; + } #endif /* @@ -1534,7 +1555,7 @@ after: return 1; } -#ifdef LWS_WITH_CGI +#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY) deal_body: #endif /* @@ -1547,7 +1568,7 @@ deal_body: */ if (lwsi_state(wsi) != LRS_ISSUING_FILE) { /* Prepare to read body if we have a content length: */ - lwsl_debug("wsi->http.rx_content_length %lld %d %d\n", + lwsl_notice("wsi->http.rx_content_length %lld %d %d\n", (long long)wsi->http.rx_content_length, wsi->upgraded_to_http2, wsi->http2_substream);