diff --git a/qa/L0_http/http_request_many_chunks.py b/qa/L0_http/http_request_many_chunks.py index d733fb000c..aef2ed97b1 100755 --- a/qa/L0_http/http_request_many_chunks.py +++ b/qa/L0_http/http_request_many_chunks.py @@ -30,7 +30,11 @@ import unittest sys.path.append("../common") -from test_util import MIB, get_server_process_from_env, wait_for_stable_rss +from test_util import ( # noqa: E402 + MIB, + get_server_process_from_env, + wait_for_stable_rss, +) class HTTPRequestManyChunksTest(unittest.TestCase): @@ -142,7 +146,7 @@ def test_chunked_infer_over_max_chunks_reject_with_bounded_rss_growth(self): ) # Wait until RSS is stable across several measurements before continuing. server = get_server_process_from_env("SERVER_PID") - wait_for_stable_rss(server) + wait_for_stable_rss(server, stable_threshold=100) # Monitor RSS growth over 100 requests. repeat_request_count = 100 diff --git a/src/http_server.cc b/src/http_server.cc index bd7262ed80..80f3c2edfd 100644 --- a/src/http_server.cc +++ b/src/http_server.cc @@ -281,6 +281,10 @@ HTTPServer::StopCallback(evutil_socket_t sock, short events, void* arg) void HTTPServer::Dispatch(evhtp_request_t* req, void* arg) { + // A parser hook may have replied before the buffered request body finishes. + if ((req->flags & EVHTP_REQ_FLAG_FINISHED) != 0) { + return; + } (static_cast(arg))->Handle(req); } @@ -317,6 +321,13 @@ HTTPServer::ChunkCountIncrement( return EVHTP_RES_OK; } if (++req->chunk_count > kMaxChunkedChunks) { + // Release the accumulated body before allocating the error response. The + // parser can still consume bytes from its current read buffer after reads + // are disabled, so discard those body fragments instead of retaining them. + evbuffer_drain(req->buffer_in, evbuffer_get_length(req->buffer_in)); + evhtp_request_set_hook( + req, evhtp_hook_on_read, + (evhtp_hook)(void*)HTTPServer::DiscardRequestBody, nullptr); AddContentTypeHeader(req, "application/json"); const std::string msg = std::string("Chunked request body exceeds maximum of ") + @@ -336,6 +347,16 @@ HTTPServer::ChunkCountIncrement( return EVHTP_RES_OK; } +evhtp_res +HTTPServer::DiscardRequestBody( + evhtp_request_t* req, evbuffer* buffer, void* arg) +{ + (void)req; + (void)arg; + evbuffer_drain(buffer, evbuffer_get_length(buffer)); + return EVHTP_RES_OK; +} + evhtp_res HTTPServer::EndConnection(evhtp_connection_t* conn, void* arg) { diff --git a/src/http_server.h b/src/http_server.h index f45d07f4e7..c14afc8f21 100644 --- a/src/http_server.h +++ b/src/http_server.h @@ -108,6 +108,8 @@ class HTTPServer { // Transfer-Encoding: chunked — increment count per non-empty chunk (capped). static evhtp_res ChunkCountIncrement( evhtp_request_t* req, uint64_t chunk_len, void* arg); + static evhtp_res DiscardRequestBody( + evhtp_request_t* req, evbuffer* buffer, void* arg); static evhtp_res NewConnection(evhtp_connection_t* conn, void* arg); static evhtp_res EndConnection(evhtp_connection_t* conn, void* arg);