Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
32 changes: 28 additions & 4 deletions http/server/HttpHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -105,23 +110,42 @@ bool HttpHandler::Init(int http_version) {
break;
}
};
return true;
}

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();
// 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<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());
hookHttpCb();
}

void HttpHandler::Close() {
Expand Down
1 change: 1 addition & 0 deletions http/server/HttpHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ class HttpHandler {
void addResponseHeaders();

// http_cb
void hookHttpCb();
void onHeadersComplete();
void onBody(const char* data, size_t size);
void onMessageComplete();
Expand Down