From: Matus Fabian Date: Thu, 19 Jun 2025 13:48:04 +0000 (+0000) Subject: http: client can receive response while sending X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=vpp.git http: client can receive response while sending Client can receive response (error) from server while still sending body bytes, handle this as exception in state machine instead of error. Type: improvement Change-Id: I6aa3f7f5aaa299ac781109dd75295a7eb3a42cf9 Signed-off-by: Matus Fabian --- diff --git a/extras/hs-test/http_test.go b/extras/hs-test/http_test.go index 60472709807..8670b1deacd 100644 --- a/extras/hs-test/http_test.go +++ b/extras/hs-test/http_test.go @@ -38,7 +38,7 @@ func init() { HttpRequestLineTest, HttpClientGetTimeout, HttpStaticFileHandlerWrkTest, HttpStaticUrlHandlerWrkTest, HttpConnTimeoutTest, HttpClientGetRepeatTest, HttpClientPostRepeatTest, HttpIgnoreH2UpgradeTest, HttpInvalidAuthorityFormUriTest, HttpHeaderErrorConnectionDropTest, HttpClientInvalidHeaderNameTest, HttpStaticHttp1OnlyTest, HttpTimerSessionDisable, HttpClientBodySizeTest, - HttpStaticRedirectTest, HttpClientNoPrintTest, HttpClientChunkedDownloadTest) + HttpStaticRedirectTest, HttpClientNoPrintTest, HttpClientChunkedDownloadTest, HttpClientPostRejectedTest) RegisterNoTopoSoloTests(HttpStaticPromTest, HttpGetTpsTest, HttpGetTpsInterruptModeTest, PromConcurrentConnectionsTest, PromMemLeakTest, HttpClientPostMemLeakTest, HttpInvalidClientRequestMemLeakTest, HttpPostTpsTest, HttpPostTpsInterruptModeTest, PromConsecutiveConnectionsTest, HttpGetTpsTlsTest, HttpPostTpsTlsTest) @@ -793,6 +793,36 @@ func HttpClientPostFilePtrTest(s *NoTopoSuite) { httpClientPostFile(s, true, 131072) } +func HttpClientPostRejectedTest(s *NoTopoSuite) { + serverAddress := s.HostAddr() + ":" + s.Ports.Http + vpp := s.Containers.Vpp.VppInstance + fileName := "/tmp/test_file.txt" + // send something big so we are sure that server respond when we are still sending body + s.Log(vpp.Container.Exec(false, "fallocate -l "+strconv.Itoa(10<<20)+" "+fileName)) + s.Log(vpp.Container.Exec(false, "ls -la "+fileName)) + + server := ghttp.NewUnstartedServer() + l, err := net.Listen("tcp", serverAddress) + s.AssertNil(err, fmt.Sprint(err)) + server.HTTPTestServer.Listener = l + server.AppendHandlers( + ghttp.CombineHandlers( + s.LogHttpReq(false), + ghttp.VerifyRequest("POST", "/test"), + ghttp.RespondWith(http.StatusForbidden, nil), + )) + server.Start() + defer server.Close() + + uri := "http://" + serverAddress + "/test" + cmd := "http client post verbose uri " + uri + " file " + fileName + o := vpp.Vppctl(cmd) + + s.Log(o) + s.AssertContains(o, "403") + s.Log(vpp.Vppctl("show session verbose 2")) +} + func HttpStaticPromTest(s *NoTopoSuite) { query := "stats.prom" vpp := s.Containers.Vpp.VppInstance diff --git a/src/plugins/hs_apps/http_client.c b/src/plugins/hs_apps/http_client.c index d49c6a4429a..937d98edae8 100644 --- a/src/plugins/hs_apps/http_client.c +++ b/src/plugins/hs_apps/http_client.c @@ -483,6 +483,8 @@ hc_rx_callback (session_t *s) if (msg.data.body_len == 0) { svm_fifo_dequeue_drop_all (s->rx_fifo); + /* we don't need to print warning about binary content */ + hc_session->session_flags |= HC_S_FLAG_PRINTABLE_BODY; goto done; } diff --git a/src/plugins/http/http1.c b/src/plugins/http/http1.c index a0aaf067da9..106a1fdc526 100644 --- a/src/plugins/http/http1.c +++ b/src/plugins/http/http1.c @@ -1975,15 +1975,30 @@ http1_transport_rx_callback (http_conn_t *hc) if (!http1_req_state_is_rx_valid (req)) { if (http_io_ts_max_read (hc)) - clib_warning ("hc [%u]%x invalid rx state: http req state " - "'%U', session state '%U'", - hc->c_thread_index, hc->hc_hc_index, - format_http_req_state, req->state, - format_http_conn_state, hc); - http_io_ts_drain_all (hc); + { + if (req->state == HTTP_REQ_STATE_APP_IO_MORE_DATA && + !(hc->flags & HTTP_CONN_F_IS_SERVER)) + { + /* client can receive error response from server when still + * sending content */ + /* TODO: 100 continue support */ + HTTP_DBG (1, "server send response while client sending data"); + http_io_as_drain_all (req); + hc->state = HTTP_CONN_STATE_CLOSED; + http_req_state_change (req, HTTP_REQ_STATE_WAIT_TRANSPORT_REPLY); + goto run_sm; + } + clib_warning ("hc [%u]%x invalid rx state: http req state " + "'%U', session state '%U'", + hc->c_thread_index, hc->hc_hc_index, + format_http_req_state, req->state, + format_http_conn_state, hc); + http_io_ts_drain_all (hc); + } return; } +run_sm: HTTP_DBG (1, "run state machine"); http1_req_run_state_machine (hc, req, 0, 0); }