Consistently use state->arena for http handling
This commit is contained in:
@@ -159,29 +159,26 @@ void HttpHandler::on_data_arrived(std::string_view data, Connection &conn) {
|
||||
llhttp_execute(&state->parser, data.data(), data.size());
|
||||
|
||||
if (err != HPE_OK) {
|
||||
send_error_response(conn, 400, "Bad request", Arena{}, 0, true);
|
||||
send_error_response(conn, 400, "Bad request", std::move(state->arena), 0,
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
// If message is complete, route and handle the request
|
||||
if (state->message_complete) {
|
||||
// Create request-scoped arena for URL parsing
|
||||
// FIX: request_arena lifetime ends too soon. Should move arena into
|
||||
// individual handlers and propagate it all the way through to
|
||||
// append_message
|
||||
Arena request_arena;
|
||||
char *url_buffer = request_arena.allocate<char>(state->url.size());
|
||||
// Use connection state's arena for all request processing
|
||||
char *url_buffer = state->arena.allocate<char>(state->url.size());
|
||||
std::memcpy(url_buffer, state->url.data(), state->url.size());
|
||||
|
||||
RouteMatch route_match;
|
||||
auto parse_result =
|
||||
ApiUrlParser::parse(state->method, url_buffer,
|
||||
static_cast<int>(state->url.size()), route_match);
|
||||
auto parse_result = ApiUrlParser::parse(
|
||||
state->method, const_cast<char *>(state->url.data()),
|
||||
static_cast<int>(state->url.size()), route_match);
|
||||
|
||||
if (parse_result != ParseResult::Success) {
|
||||
// Handle malformed URL encoding
|
||||
send_error_response(conn, 400, "Malformed URL encoding", Arena{}, 0,
|
||||
true);
|
||||
send_error_response(conn, 400, "Malformed URL encoding",
|
||||
std::move(state->arena), 0, true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,36 +187,35 @@ void HttpHandler::on_data_arrived(std::string_view data, Connection &conn) {
|
||||
// Route to appropriate handler
|
||||
switch (state->route) {
|
||||
case HttpRoute::GetVersion:
|
||||
handle_get_version(conn, *state, std::move(request_arena));
|
||||
handle_get_version(conn, *state);
|
||||
break;
|
||||
case HttpRoute::PostCommit:
|
||||
handle_post_commit(conn, *state, std::move(request_arena));
|
||||
handle_post_commit(conn, *state);
|
||||
break;
|
||||
case HttpRoute::GetSubscribe:
|
||||
handle_get_subscribe(conn, *state, std::move(request_arena));
|
||||
handle_get_subscribe(conn, *state);
|
||||
break;
|
||||
case HttpRoute::GetStatus:
|
||||
handle_get_status(conn, *state, route_match, std::move(request_arena));
|
||||
handle_get_status(conn, *state, route_match);
|
||||
break;
|
||||
case HttpRoute::PutRetention:
|
||||
handle_put_retention(conn, *state, route_match, std::move(request_arena));
|
||||
handle_put_retention(conn, *state, route_match);
|
||||
break;
|
||||
case HttpRoute::GetRetention:
|
||||
handle_get_retention(conn, *state, route_match, std::move(request_arena));
|
||||
handle_get_retention(conn, *state, route_match);
|
||||
break;
|
||||
case HttpRoute::DeleteRetention:
|
||||
handle_delete_retention(conn, *state, route_match,
|
||||
std::move(request_arena));
|
||||
handle_delete_retention(conn, *state, route_match);
|
||||
break;
|
||||
case HttpRoute::GetMetrics:
|
||||
handle_get_metrics(conn, *state, std::move(request_arena));
|
||||
handle_get_metrics(conn, *state);
|
||||
break;
|
||||
case HttpRoute::GetOk:
|
||||
handle_get_ok(conn, *state, std::move(request_arena));
|
||||
handle_get_ok(conn, *state);
|
||||
break;
|
||||
case HttpRoute::NotFound:
|
||||
default:
|
||||
handle_not_found(conn, *state, std::move(request_arena));
|
||||
handle_not_found(conn, *state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -227,28 +223,21 @@ void HttpHandler::on_data_arrived(std::string_view data, Connection &conn) {
|
||||
|
||||
// Route handlers (basic implementations)
|
||||
void HttpHandler::handle_get_version(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
Arena request_arena) {
|
||||
HttpConnectionState &state) {
|
||||
version_counter.inc();
|
||||
send_json_response(
|
||||
conn, 200,
|
||||
format(request_arena, R"({"version":%ld,"leader":""})",
|
||||
format(state.arena, R"({"version":%ld,"leader":""})",
|
||||
this->committed_version.load(std::memory_order_seq_cst)),
|
||||
std::move(request_arena), state.http_request_id, state.connection_close);
|
||||
std::move(state.arena), state.http_request_id, state.connection_close);
|
||||
}
|
||||
|
||||
void HttpHandler::handle_post_commit(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
Arena request_arena) {
|
||||
HttpConnectionState &state) {
|
||||
commit_counter.inc();
|
||||
// Check if streaming parse was successful
|
||||
if (!state.commit_request || !state.parsing_commit) {
|
||||
const char *error = state.commit_parser
|
||||
? state.commit_parser->get_parse_error()
|
||||
: "No parser initialized";
|
||||
std::string_view error_msg = format(request_arena, "Parse failed: %s",
|
||||
error ? error : "Unknown error");
|
||||
send_error_response(conn, 400, error_msg, std::move(request_arena),
|
||||
send_error_response(conn, 400, "Parse failed", std::move(state.arena),
|
||||
state.http_request_id, state.connection_close);
|
||||
return;
|
||||
}
|
||||
@@ -289,7 +278,7 @@ void HttpHandler::handle_post_commit(Connection &conn,
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
send_error_response(conn, 400, error_msg, std::move(request_arena),
|
||||
send_error_response(conn, 400, error_msg, std::move(state.arena),
|
||||
state.http_request_id, state.connection_close);
|
||||
return;
|
||||
}
|
||||
@@ -300,18 +289,17 @@ void HttpHandler::handle_post_commit(Connection &conn,
|
||||
}
|
||||
|
||||
void HttpHandler::handle_get_subscribe(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
Arena) {
|
||||
HttpConnectionState &state) {
|
||||
// TODO: Implement subscription streaming
|
||||
send_json_response(
|
||||
conn, 200,
|
||||
R"({"message":"Subscription endpoint - streaming not yet implemented"})",
|
||||
Arena{}, state.http_request_id, state.connection_close);
|
||||
std::move(state.arena), state.http_request_id, state.connection_close);
|
||||
}
|
||||
|
||||
void HttpHandler::handle_get_status(Connection &conn,
|
||||
HttpConnectionState &state,
|
||||
const RouteMatch &route_match, Arena) {
|
||||
const RouteMatch &route_match) {
|
||||
status_counter.inc();
|
||||
// Status requests are processed through the pipeline
|
||||
// Response will be generated in the sequence stage
|
||||
@@ -321,15 +309,16 @@ void HttpHandler::handle_get_status(Connection &conn,
|
||||
const auto &request_id =
|
||||
route_match.params[static_cast<int>(ApiParameterKey::RequestId)];
|
||||
if (!request_id) {
|
||||
send_error_response(conn, 400,
|
||||
"Missing required query parameter: request_id", Arena{},
|
||||
state.http_request_id, state.connection_close);
|
||||
send_error_response(
|
||||
conn, 400, "Missing required query parameter: request_id",
|
||||
std::move(state.arena), state.http_request_id, state.connection_close);
|
||||
return;
|
||||
}
|
||||
|
||||
if (request_id->empty()) {
|
||||
send_error_response(conn, 400, "Empty request_id parameter", Arena{},
|
||||
state.http_request_id, state.connection_close);
|
||||
send_error_response(conn, 400, "Empty request_id parameter",
|
||||
std::move(state.arena), state.http_request_id,
|
||||
state.connection_close);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -340,34 +329,35 @@ void HttpHandler::handle_get_status(Connection &conn,
|
||||
}
|
||||
|
||||
void HttpHandler::handle_put_retention(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
const RouteMatch &, Arena) {
|
||||
HttpConnectionState &state,
|
||||
const RouteMatch &) {
|
||||
// TODO: Parse retention policy from body and store
|
||||
send_json_response(conn, 200, R"({"policy_id":"example","status":"created"})",
|
||||
Arena{}, state.http_request_id, state.connection_close);
|
||||
std::move(state.arena), state.http_request_id,
|
||||
state.connection_close);
|
||||
}
|
||||
|
||||
void HttpHandler::handle_get_retention(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
const RouteMatch &, Arena) {
|
||||
HttpConnectionState &state,
|
||||
const RouteMatch &) {
|
||||
// TODO: Extract policy_id from URL or return all policies
|
||||
send_json_response(conn, 200, R"({"policies":[]})", Arena{},
|
||||
send_json_response(conn, 200, R"({"policies":[]})", std::move(state.arena),
|
||||
state.http_request_id, state.connection_close);
|
||||
}
|
||||
|
||||
void HttpHandler::handle_delete_retention(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
const RouteMatch &, Arena) {
|
||||
HttpConnectionState &state,
|
||||
const RouteMatch &) {
|
||||
// TODO: Extract policy_id from URL and delete
|
||||
send_json_response(conn, 200, R"({"policy_id":"example","status":"deleted"})",
|
||||
Arena{}, state.http_request_id, state.connection_close);
|
||||
std::move(state.arena), state.http_request_id,
|
||||
state.connection_close);
|
||||
}
|
||||
|
||||
void HttpHandler::handle_get_metrics(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
Arena request_arena) {
|
||||
HttpConnectionState &state) {
|
||||
metrics_counter.inc();
|
||||
auto metrics_span = metric::render(request_arena);
|
||||
auto metrics_span = metric::render(state.arena);
|
||||
|
||||
// Calculate total size for the response body
|
||||
size_t total_size = 0;
|
||||
@@ -381,14 +371,14 @@ void HttpHandler::handle_get_metrics(Connection &conn,
|
||||
std::string_view headers;
|
||||
if (state.connection_close) {
|
||||
headers = static_format(
|
||||
request_arena, "HTTP/1.1 200 OK\r\n",
|
||||
state.arena, "HTTP/1.1 200 OK\r\n",
|
||||
"Content-Type: text/plain; version=0.0.4\r\n",
|
||||
"Content-Length: ", static_cast<uint64_t>(total_size), "\r\n",
|
||||
"X-Response-ID: ", static_cast<int64_t>(http_state->http_request_id),
|
||||
"\r\n", "Connection: close\r\n", "\r\n");
|
||||
} else {
|
||||
headers = static_format(
|
||||
request_arena, "HTTP/1.1 200 OK\r\n",
|
||||
state.arena, "HTTP/1.1 200 OK\r\n",
|
||||
"Content-Type: text/plain; version=0.0.4\r\n",
|
||||
"Content-Length: ", static_cast<uint64_t>(total_size), "\r\n",
|
||||
"X-Response-ID: ", static_cast<int64_t>(http_state->http_request_id),
|
||||
@@ -396,18 +386,17 @@ void HttpHandler::handle_get_metrics(Connection &conn,
|
||||
}
|
||||
|
||||
auto result = std::span<std::string_view>{
|
||||
request_arena.allocate<std::string_view>(metrics_span.size() + 1),
|
||||
state.arena.allocate<std::string_view>(metrics_span.size() + 1),
|
||||
metrics_span.size() + 1};
|
||||
auto out = result.begin();
|
||||
*out++ = headers;
|
||||
for (auto sv : metrics_span) {
|
||||
*out++ = sv;
|
||||
}
|
||||
conn.append_message(result, std::move(request_arena));
|
||||
conn.append_message(result, std::move(state.arena));
|
||||
}
|
||||
|
||||
void HttpHandler::handle_get_ok(Connection &, const HttpConnectionState &state,
|
||||
Arena) {
|
||||
void HttpHandler::handle_get_ok(Connection &, HttpConnectionState &state) {
|
||||
ok_counter.inc();
|
||||
TRACE_EVENT("http", "GET /ok", perfetto::Flow::Global(state.http_request_id));
|
||||
|
||||
@@ -416,9 +405,9 @@ void HttpHandler::handle_get_ok(Connection &, const HttpConnectionState &state,
|
||||
}
|
||||
|
||||
void HttpHandler::handle_not_found(Connection &conn,
|
||||
const HttpConnectionState &state, Arena) {
|
||||
send_error_response(conn, 404, "Not found", Arena{}, state.http_request_id,
|
||||
state.connection_close);
|
||||
HttpConnectionState &state) {
|
||||
send_error_response(conn, 404, "Not found", std::move(state.arena),
|
||||
state.http_request_id, state.connection_close);
|
||||
}
|
||||
|
||||
// HTTP utility methods
|
||||
|
||||
@@ -25,7 +25,7 @@ struct RouteMatch;
|
||||
* Manages llhttp parser state and request data.
|
||||
*/
|
||||
struct HttpConnectionState {
|
||||
Arena arena; // Request-scoped arena for parsing state
|
||||
Arena arena{16 << 10}; // Request-scoped arena for parsing state
|
||||
llhttp_t parser;
|
||||
llhttp_settings_t settings;
|
||||
|
||||
@@ -168,10 +168,11 @@ private:
|
||||
BannedRequestIdSet banned_request_ids; // Request IDs that should not commit
|
||||
// (string_views into arena)
|
||||
|
||||
constexpr static auto wait_strategy = WaitStrategy::WaitIfUpstreamIdle;
|
||||
|
||||
// Main commit processing pipeline: sequence -> resolve -> persist -> release
|
||||
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfStageEmpty, 1, 1, 1,
|
||||
1>
|
||||
commitPipeline{lg_size};
|
||||
StaticThreadPipeline<PipelineEntry, wait_strategy, 1, 1, 1, 1> commitPipeline{
|
||||
lg_size};
|
||||
|
||||
// Pipeline stage threads
|
||||
std::thread sequenceThread;
|
||||
@@ -181,36 +182,27 @@ private:
|
||||
|
||||
// Pipeline stage processing methods (batch-based)
|
||||
using BatchType =
|
||||
StaticThreadPipeline<PipelineEntry, WaitStrategy::WaitIfStageEmpty, 1, 1,
|
||||
1, 1>::Batch;
|
||||
StaticThreadPipeline<PipelineEntry, wait_strategy, 1, 1, 1, 1>::Batch;
|
||||
bool process_sequence_batch(BatchType &batch);
|
||||
bool process_resolve_batch(BatchType &batch);
|
||||
bool process_persist_batch(BatchType &batch);
|
||||
bool process_release_batch(BatchType &batch);
|
||||
|
||||
// Route handlers
|
||||
void handle_get_version(Connection &conn, const HttpConnectionState &state,
|
||||
Arena request_arena);
|
||||
void handle_post_commit(Connection &conn, const HttpConnectionState &state,
|
||||
Arena request_arena);
|
||||
void handle_get_subscribe(Connection &conn, const HttpConnectionState &state,
|
||||
Arena request_arena);
|
||||
void handle_get_version(Connection &conn, HttpConnectionState &state);
|
||||
void handle_post_commit(Connection &conn, HttpConnectionState &state);
|
||||
void handle_get_subscribe(Connection &conn, HttpConnectionState &state);
|
||||
void handle_get_status(Connection &conn, HttpConnectionState &state,
|
||||
const RouteMatch &route_match, Arena request_arena);
|
||||
void handle_put_retention(Connection &conn, const HttpConnectionState &state,
|
||||
const RouteMatch &route_match, Arena request_arena);
|
||||
void handle_get_retention(Connection &conn, const HttpConnectionState &state,
|
||||
const RouteMatch &route_match, Arena request_arena);
|
||||
void handle_delete_retention(Connection &conn,
|
||||
const HttpConnectionState &state,
|
||||
const RouteMatch &route_match,
|
||||
Arena request_arena);
|
||||
void handle_get_metrics(Connection &conn, const HttpConnectionState &state,
|
||||
Arena request_arena);
|
||||
void handle_get_ok(Connection &conn, const HttpConnectionState &state,
|
||||
Arena request_arena);
|
||||
void handle_not_found(Connection &conn, const HttpConnectionState &state,
|
||||
Arena request_arena);
|
||||
const RouteMatch &route_match);
|
||||
void handle_put_retention(Connection &conn, HttpConnectionState &state,
|
||||
const RouteMatch &route_match);
|
||||
void handle_get_retention(Connection &conn, HttpConnectionState &state,
|
||||
const RouteMatch &route_match);
|
||||
void handle_delete_retention(Connection &conn, HttpConnectionState &state,
|
||||
const RouteMatch &route_match);
|
||||
void handle_get_metrics(Connection &conn, HttpConnectionState &state);
|
||||
void handle_get_ok(Connection &conn, HttpConnectionState &state);
|
||||
void handle_not_found(Connection &conn, HttpConnectionState &state);
|
||||
|
||||
// HTTP utilities
|
||||
static void send_response(MessageSender &conn, int status_code,
|
||||
|
||||
@@ -10,9 +10,8 @@ interfaces = [
|
||||
max_request_size_bytes = 1048576 # 1MB
|
||||
# Number of I/O threads for handling connections and network events
|
||||
io_threads = 8
|
||||
epoll_instances = 8
|
||||
# Event batch size for epoll processing
|
||||
event_batch_size = 64
|
||||
event_batch_size = 128
|
||||
|
||||
[commit]
|
||||
# Minimum length for request_id to ensure sufficient entropy
|
||||
|
||||
Reference in New Issue
Block a user