From 35c78ccb4102544d0cf2cca1e5ed0619a9c9fce3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:54:52 +0000 Subject: [PATCH 1/4] Initial plan From 4545e195bdd1fe1f8c9b44851ff954282b32aadf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:59:05 +0000 Subject: [PATCH 2/4] Fix HTTP pipeline async response crash by creating new req/resp per request Instead of resetting the existing HttpRequest/HttpResponse objects in HttpHandler::Reset(), create new ones. This prevents a race condition where async handlers (holding shared_ptr references to the old req/resp) could crash when FeedRecvData calls Reset() upon receiving HTTP pipeline data or an attack while the async response is still being written. The old req/resp objects remain alive through the shared_ptr references held by the async handler, while the handler gets fresh objects for the new incoming request. Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- http/server/HttpHandler.cpp | 38 +++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/http/server/HttpHandler.cpp b/http/server/HttpHandler.cpp index 29aa363c4..4602ec828 100644 --- a/http/server/HttpHandler.cpp +++ b/http/server/HttpHandler.cpp @@ -111,17 +111,43 @@ bool HttpHandler::Init(int http_version) { void HttpHandler::Reset() { state = WANT_RECV; error = 0; - req->Reset(); - resp->Reset(); + // Create new request/response to avoid race condition with async handlers + // that may still hold shared_ptr references to the old req/resp objects. + // This prevents crashes when HTTP pipeline data arrives while an async + // response is still being written. + req = std::make_shared(); + resp = std::make_shared(); + if (protocol == HTTP_V2) { + resp->http_major = req->http_major = 2; + resp->http_minor = req->http_minor = 0; + } ctx = NULL; api_handler = NULL; closeFile(); - if (writer) { - writer->Begin(); - writer->onwrite = NULL; - writer->onclose = NULL; + if (io) { + writer = std::make_shared(io, resp); + writer->status = hv::SocketChannel::CONNECTED; } parser->InitRequest(req.get()); + // Re-hook http_cb for the new request object + req->http_cb = [this](HttpMessage* msg, http_parser_state state, const char* data, size_t size) { + if (this->state == WANT_CLOSE) return; + switch (state) { + case HP_HEADERS_COMPLETE: + if (this->error != 0) return; + onHeadersComplete(); + break; + case HP_BODY: + if (this->error != 0) return; + onBody(data, size); + break; + case HP_MESSAGE_COMPLETE: + onMessageComplete(); + break; + default: + break; + } + }; } void HttpHandler::Close() { From 93e11a72bc0c809e8040163de4f848809a59df78 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 14:00:01 +0000 Subject: [PATCH 3/4] Add defensive null check for writer when io is null in Reset() Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- http/server/HttpHandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http/server/HttpHandler.cpp b/http/server/HttpHandler.cpp index 4602ec828..d29511453 100644 --- a/http/server/HttpHandler.cpp +++ b/http/server/HttpHandler.cpp @@ -127,6 +127,8 @@ void HttpHandler::Reset() { if (io) { writer = std::make_shared(io, resp); writer->status = hv::SocketChannel::CONNECTED; + } else { + writer = NULL; } parser->InitRequest(req.get()); // Re-hook http_cb for the new request object From 5084c451aa55ca7ec6a6d6af470d4ec3af491903 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 04:20:58 +0000 Subject: [PATCH 4/4] Retire old writer before replacing to prevent hio_t close; extract hookHttpCb helper Before creating a new HttpResponseWriter in Reset(), mark the old writer as DISCONNECTED and clear its io_ pointer. This ensures: - ~Channel() won't close the hio_t when the async handler's shared_ptr reference is eventually released (isOpened() returns false) - Any further write attempts through the retired writer fail gracefully (write() returns -1) instead of interfering with the new request Also extract the duplicated http_cb lambda from Init() and Reset() into a private hookHttpCb() helper method. Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- http/server/HttpHandler.cpp | 36 ++++++++++++++++-------------------- http/server/HttpHandler.h | 1 + 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/http/server/HttpHandler.cpp b/http/server/HttpHandler.cpp index d29511453..71a96230a 100644 --- a/http/server/HttpHandler.cpp +++ b/http/server/HttpHandler.cpp @@ -86,6 +86,11 @@ bool HttpHandler::Init(int http_version) { tid = hv_gettid(); } parser->InitRequest(req.get()); + hookHttpCb(); + return true; +} + +void HttpHandler::hookHttpCb() { // NOTE: hook http_cb req->http_cb = [this](HttpMessage* msg, http_parser_state state, const char* data, size_t size) { if (this->state == WANT_CLOSE) return; @@ -105,7 +110,6 @@ bool HttpHandler::Init(int http_version) { break; } }; - return true; } void HttpHandler::Reset() { @@ -124,6 +128,16 @@ void HttpHandler::Reset() { ctx = NULL; api_handler = NULL; closeFile(); + if (writer) { + // Retire old writer: mark DISCONNECTED and clear io_ so that + // ~Channel() won't close the hio_t when the async handler's + // shared_ptr reference is eventually released, and any + // further write attempts through the old writer fail gracefully. + writer->status = hv::SocketChannel::DISCONNECTED; + writer->io_ = NULL; + writer->onwrite = NULL; + writer->onclose = NULL; + } if (io) { writer = std::make_shared(io, resp); writer->status = hv::SocketChannel::CONNECTED; @@ -131,25 +145,7 @@ void HttpHandler::Reset() { writer = NULL; } parser->InitRequest(req.get()); - // Re-hook http_cb for the new request object - req->http_cb = [this](HttpMessage* msg, http_parser_state state, const char* data, size_t size) { - if (this->state == WANT_CLOSE) return; - switch (state) { - case HP_HEADERS_COMPLETE: - if (this->error != 0) return; - onHeadersComplete(); - break; - case HP_BODY: - if (this->error != 0) return; - onBody(data, size); - break; - case HP_MESSAGE_COMPLETE: - onMessageComplete(); - break; - default: - break; - } - }; + hookHttpCb(); } void HttpHandler::Close() { diff --git a/http/server/HttpHandler.h b/http/server/HttpHandler.h index b72f07772..f9b1ba8ae 100644 --- a/http/server/HttpHandler.h +++ b/http/server/HttpHandler.h @@ -149,6 +149,7 @@ class HttpHandler { void addResponseHeaders(); // http_cb + void hookHttpCb(); void onHeadersComplete(); void onBody(const char* data, size_t size); void onMessageComplete();