Skip to content
Open
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions http/server/HttpHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,45 @@ 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<HttpRequest>();
resp = std::make_shared<HttpResponse>();
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<HttpResponseWriter>(io, resp);
writer->status = hv::SocketChannel::CONNECTED;
} else {
writer = NULL;
Comment on lines +141 to +145
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reset() now replaces writer with a new HttpResponseWriter bound to the same hio_t. The old writer will be destroyed as soon as its last shared_ptr is released, and hv::Channel::~Channel() closes the underlying hio_t when isOpened() is true—this will break keep-alive (old writer destroyed immediately on normal Reset) and can also close an active connection while a subsequent request is being processed. Additionally, constructing a new Channel/SocketChannel on the same hio_t overwrites hio_context, so on_write/on_close callbacks will no longer be routed to the in-flight writer instance. Consider keeping a single Channel wrapper per connection and making per-request writers non-owning (no hio_context mutation / no close-on-destroy), or otherwise ensure retiring writers cannot close or steal callbacks from the connection while still allowing the active writer to finish sending.

Copilot uses AI. Check for mistakes.
}
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;
}
};
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The http_cb lambda is duplicated in both Init() and Reset(). Since this callback wiring is now required whenever a new HttpRequest is allocated, consider extracting it into a small helper (e.g., a private method) to reduce duplication and the risk of future divergence between the two copies.

Copilot uses AI. Check for mistakes.
}

void HttpHandler::Close() {
Expand Down