Separate HttpRequestState and HttpConnectionState

Now HttpConnectionState has a queue of HttpRequestState
This commit is contained in:
2025-09-14 23:49:32 -04:00
parent fac6b8de88
commit 022a79bf5b
2 changed files with 143 additions and 210 deletions

View File

@@ -36,10 +36,7 @@ auto banned_request_ids_memory_gauge =
"Memory used by banned request IDs arena") "Memory used by banned request IDs arena")
.create({}); .create({});
// HttpConnectionState implementation HttpConnectionState::HttpConnectionState() {
HttpConnectionState::HttpConnectionState()
: current_header_field_buf(ArenaStlAllocator<char>(&arena)),
current_header_value_buf(ArenaStlAllocator<char>(&arena)) {
llhttp_settings_init(&settings); llhttp_settings_init(&settings);
// Set up llhttp callbacks // Set up llhttp callbacks
@@ -53,40 +50,13 @@ HttpConnectionState::HttpConnectionState()
settings.on_message_complete = HttpHandler::onMessageComplete; settings.on_message_complete = HttpHandler::onMessageComplete;
llhttp_init(&parser, HTTP_REQUEST, &settings); llhttp_init(&parser, HTTP_REQUEST, &settings);
parser.data = this; parser.data = &pending;
} }
void HttpConnectionState::reset() { // HttpConnectionState implementation
TRACE_EVENT("http", "reply", perfetto::Flow::Global(http_request_id)); HttpRequestState::HttpRequestState()
// Reset request-specific state for next HTTP request : current_header_field_buf(ArenaStlAllocator<char>(&arena)),
method = {}; current_header_value_buf(ArenaStlAllocator<char>(&arena)) {}
url = {};
headers_complete = false;
message_complete = false;
connection_close = false;
route = HttpRoute::NotFound;
status_request_id = {};
// Reset header buffers - need to recreate with arena allocator
current_header_field_buf = ArenaString(ArenaStlAllocator<char>(&arena));
current_header_value_buf = ArenaString(ArenaStlAllocator<char>(&arena));
header_field_complete = false;
http_request_id = 0;
// Reset commit parsing state - safe to reset Arena::Ptr objects here
// since request processing is complete
commit_parser.reset();
commit_request.reset();
parsing_commit = false;
basic_validation_passed = false;
// Reset arena memory for next request to prevent memory growth
arena.reset();
// Reset llhttp parser for next request
llhttp_init(&parser, HTTP_REQUEST, &settings);
parser.data = this;
}
// HttpHandler implementation // HttpHandler implementation
void HttpHandler::on_connection_established(Connection &conn) { void HttpHandler::on_connection_established(Connection &conn) {
@@ -101,68 +71,94 @@ void HttpHandler::on_connection_closed(Connection &conn) {
conn.user_data = nullptr; conn.user_data = nullptr;
} }
static thread_local std::vector<PipelineEntry> g_batch_entries;
void HttpHandler::on_batch_complete(std::span<Connection *const> batch) { void HttpHandler::on_batch_complete(std::span<Connection *const> batch) {
// Collect commit, status, and health check requests for pipeline processing
int pipeline_count = 0;
// Count commit, status, and health check requests // Count commit, status, and health check requests
for (auto conn : batch) { for (auto conn : batch) {
auto *state = static_cast<HttpConnectionState *>(conn->user_data); auto *state = static_cast<HttpConnectionState *>(conn->user_data);
for (auto &req : state->queue) {
// Count commit requests that passed basic validation char *url_buffer = req.arena.allocate<char>(req.url.size());
if (state->route == HttpRoute::PostCommit && state->commit_request && std::memcpy(url_buffer, req.url.data(), req.url.size());
state->parsing_commit && state->basic_validation_passed) { RouteMatch route_match;
pipeline_count++; auto parse_result =
} ApiUrlParser::parse(req.method, const_cast<char *>(req.url.data()),
// Count status requests static_cast<int>(req.url.size()), route_match);
else if (state->route == HttpRoute::GetStatus && if (parse_result != ParseResult::Success) {
// Error message not already queued // Handle malformed URL encoding
conn->outgoing_bytes_queued() == 0) { send_error_response(*conn, 400, "Malformed URL encoding",
pipeline_count++; std::move(req.arena), 0, true);
} break;
// Count health check requests }
else if (state->route == HttpRoute::GetOk && req.route = route_match.route;
// Error message not already queued // Route to appropriate handler
conn->outgoing_bytes_queued() == 0) { switch (req.route) {
pipeline_count++; case HttpRoute::GetVersion:
handle_get_version(*conn, req);
break;
case HttpRoute::PostCommit:
handle_post_commit(*conn, req);
break;
case HttpRoute::GetSubscribe:
handle_get_subscribe(*conn, req);
break;
case HttpRoute::GetStatus:
handle_get_status(*conn, req, route_match);
break;
case HttpRoute::PutRetention:
handle_put_retention(*conn, req, route_match);
break;
case HttpRoute::GetRetention:
handle_get_retention(*conn, req, route_match);
break;
case HttpRoute::DeleteRetention:
handle_delete_retention(*conn, req, route_match);
break;
case HttpRoute::GetMetrics:
handle_get_metrics(*conn, req);
break;
case HttpRoute::GetOk:
handle_get_ok(*conn, req);
break;
case HttpRoute::NotFound:
default:
handle_not_found(*conn, req);
break;
}
// Create CommitEntry for commit requests
if (req.route == HttpRoute::PostCommit && req.commit_request &&
req.parsing_commit && req.basic_validation_passed) {
g_batch_entries.push_back(CommitEntry{
conn->get_weak_ref(), req.http_request_id, req.connection_close,
req.commit_request.get(), std::move(req.arena)});
}
// Create StatusEntry for status requests
else if (req.route == HttpRoute::GetStatus) {
g_batch_entries.push_back(StatusEntry{
conn->get_weak_ref(), req.http_request_id, req.connection_close,
req.status_request_id, std::move(req.arena)});
}
// Create HealthCheckEntry for health check requests
else if (req.route == HttpRoute::GetOk) {
g_batch_entries.push_back(
HealthCheckEntry{conn->get_weak_ref(), req.http_request_id,
req.connection_close, std::move(req.arena)});
}
} }
state->queue.clear();
} }
// Send requests to 4-stage pipeline in batch. Batching here reduces // Send requests to 4-stage pipeline in batch. Batching here reduces
// contention on the way into the pipeline. // contention on the way into the pipeline.
if (pipeline_count > 0) { if (g_batch_entries.size() > 0) {
auto guard = commitPipeline.push(pipeline_count, true); auto guard = commitPipeline.push(g_batch_entries.size(), true);
auto out_iter = guard.batch.begin(); auto out_iter = guard.batch.begin();
std::move(g_batch_entries.begin(), g_batch_entries.end(), out_iter);
for (auto conn : batch) {
auto *state = static_cast<HttpConnectionState *>(conn->user_data);
// Create CommitEntry for commit requests
if (state->route == HttpRoute::PostCommit && state->commit_request &&
state->parsing_commit && state->basic_validation_passed) {
*out_iter++ =
CommitEntry{conn->get_weak_ref(), state->http_request_id,
state->connection_close, state->commit_request.get(),
std::move(state->arena)};
state->reset();
}
// Create StatusEntry for status requests
else if (state->route == HttpRoute::GetStatus) {
*out_iter++ =
StatusEntry{conn->get_weak_ref(), state->http_request_id,
state->connection_close, state->status_request_id,
std::move(state->arena)};
state->reset();
}
// Create HealthCheckEntry for health check requests
else if (state->route == HttpRoute::GetOk) {
*out_iter++ =
HealthCheckEntry{conn->get_weak_ref(), state->http_request_id,
state->connection_close, std::move(state->arena)};
state->reset();
}
}
} }
g_batch_entries.clear();
} }
void HttpHandler::on_data_arrived(std::string_view data, Connection &conn) { void HttpHandler::on_data_arrived(std::string_view data, Connection &conn) {
@@ -178,94 +174,45 @@ void HttpHandler::on_data_arrived(std::string_view data, Connection &conn) {
// This prevents DoS attacks via oversized HTTP requests. // This prevents DoS attacks via oversized HTTP requests.
// Parse HTTP data with llhttp // Parse HTTP data with llhttp
enum llhttp_errno err = for (;;) {
llhttp_execute(&state->parser, data.data(), data.size()); enum llhttp_errno err =
llhttp_execute(&state->parser, data.data(), data.size());
if (err != HPE_OK) { if (err == HPE_PAUSED) {
send_error_response(conn, 400, "Bad request", std::move(state->arena), 0, assert(state->pending.message_complete);
true); state->queue.push_back(std::move(state->pending));
state->reset(); state->pending = {};
int consumed = llhttp_get_error_pos(&state->parser) - data.data();
data = data.substr(consumed, data.size() - consumed);
llhttp_resume(&state->parser);
continue;
}
if (err == HPE_OK) {
break;
}
send_error_response(conn, 400, "Bad request",
std::move(state->pending.arena), 0, true);
return; return;
} }
// If message is complete, route and handle the request
if (state->message_complete) {
// 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, 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",
std::move(state->arena), 0, true);
state->reset();
return;
}
state->route = route_match.route;
// Route to appropriate handler
switch (state->route) {
case HttpRoute::GetVersion:
handle_get_version(conn, *state);
break;
case HttpRoute::PostCommit:
handle_post_commit(conn, *state);
break;
case HttpRoute::GetSubscribe:
handle_get_subscribe(conn, *state);
break;
case HttpRoute::GetStatus:
handle_get_status(conn, *state, route_match);
break;
case HttpRoute::PutRetention:
handle_put_retention(conn, *state, route_match);
break;
case HttpRoute::GetRetention:
handle_get_retention(conn, *state, route_match);
break;
case HttpRoute::DeleteRetention:
handle_delete_retention(conn, *state, route_match);
break;
case HttpRoute::GetMetrics:
handle_get_metrics(conn, *state);
break;
case HttpRoute::GetOk:
handle_get_ok(conn, *state);
break;
case HttpRoute::NotFound:
default:
handle_not_found(conn, *state);
break;
}
}
} }
// Route handlers (basic implementations) // Route handlers (basic implementations)
void HttpHandler::handle_get_version(Connection &conn, void HttpHandler::handle_get_version(Connection &conn,
HttpConnectionState &state) { HttpRequestState &state) {
version_counter.inc(); version_counter.inc();
send_json_response( send_json_response(
conn, 200, conn, 200,
format(state.arena, R"({"version":%ld,"leader":""})", format(state.arena, R"({"version":%ld,"leader":""})",
this->committed_version.load(std::memory_order_seq_cst)), this->committed_version.load(std::memory_order_seq_cst)),
std::move(state.arena), state.http_request_id, state.connection_close); std::move(state.arena), state.http_request_id, state.connection_close);
state.reset();
} }
void HttpHandler::handle_post_commit(Connection &conn, void HttpHandler::handle_post_commit(Connection &conn,
HttpConnectionState &state) { HttpRequestState &state) {
commit_counter.inc(); commit_counter.inc();
// Check if streaming parse was successful // Check if streaming parse was successful
if (!state.commit_request || !state.parsing_commit) { if (!state.commit_request || !state.parsing_commit) {
send_error_response(conn, 400, "Parse failed", std::move(state.arena), send_error_response(conn, 400, "Parse failed", std::move(state.arena),
state.http_request_id, state.connection_close); state.http_request_id, state.connection_close);
state.reset();
return; return;
} }
@@ -307,27 +254,24 @@ void HttpHandler::handle_post_commit(Connection &conn,
if (!valid) { if (!valid) {
send_error_response(conn, 400, error_msg, std::move(state.arena), send_error_response(conn, 400, error_msg, std::move(state.arena),
state.http_request_id, state.connection_close); state.http_request_id, state.connection_close);
state.reset();
return; return;
} }
// Basic validation passed - mark for 4-stage pipeline processing // Basic validation passed - mark for 4-stage pipeline processing
const_cast<HttpConnectionState &>(state).basic_validation_passed = true; const_cast<HttpRequestState &>(state).basic_validation_passed = true;
// Response will be sent after 4-stage pipeline processing is complete // Response will be sent after 4-stage pipeline processing is complete
} }
void HttpHandler::handle_get_subscribe(Connection &conn, void HttpHandler::handle_get_subscribe(Connection &conn,
HttpConnectionState &state) { HttpRequestState &state) {
// TODO: Implement subscription streaming // TODO: Implement subscription streaming
send_json_response( send_json_response(
conn, 200, conn, 200,
R"({"message":"Subscription endpoint - streaming not yet implemented"})", R"({"message":"Subscription endpoint - streaming not yet implemented"})",
std::move(state.arena), state.http_request_id, state.connection_close); std::move(state.arena), state.http_request_id, state.connection_close);
state.reset();
} }
void HttpHandler::handle_get_status(Connection &conn, void HttpHandler::handle_get_status(Connection &conn, HttpRequestState &state,
HttpConnectionState &state,
const RouteMatch &route_match) { const RouteMatch &route_match) {
status_counter.inc(); status_counter.inc();
// Status requests are processed through the pipeline // Status requests are processed through the pipeline
@@ -341,7 +285,6 @@ void HttpHandler::handle_get_status(Connection &conn,
send_error_response( send_error_response(
conn, 400, "Missing required query parameter: request_id", conn, 400, "Missing required query parameter: request_id",
std::move(state.arena), state.http_request_id, state.connection_close); std::move(state.arena), state.http_request_id, state.connection_close);
state.reset();
return; return;
} }
@@ -349,7 +292,6 @@ void HttpHandler::handle_get_status(Connection &conn,
send_error_response(conn, 400, "Empty request_id parameter", send_error_response(conn, 400, "Empty request_id parameter",
std::move(state.arena), state.http_request_id, std::move(state.arena), state.http_request_id,
state.connection_close); state.connection_close);
state.reset();
return; return;
} }
@@ -360,36 +302,33 @@ void HttpHandler::handle_get_status(Connection &conn,
} }
void HttpHandler::handle_put_retention(Connection &conn, void HttpHandler::handle_put_retention(Connection &conn,
HttpConnectionState &state, HttpRequestState &state,
const RouteMatch &) { const RouteMatch &) {
// TODO: Parse retention policy from body and store // TODO: Parse retention policy from body and store
send_json_response(conn, 200, R"({"policy_id":"example","status":"created"})", send_json_response(conn, 200, R"({"policy_id":"example","status":"created"})",
std::move(state.arena), state.http_request_id, std::move(state.arena), state.http_request_id,
state.connection_close); state.connection_close);
state.reset();
} }
void HttpHandler::handle_get_retention(Connection &conn, void HttpHandler::handle_get_retention(Connection &conn,
HttpConnectionState &state, HttpRequestState &state,
const RouteMatch &) { const RouteMatch &) {
// TODO: Extract policy_id from URL or return all policies // TODO: Extract policy_id from URL or return all policies
send_json_response(conn, 200, R"({"policies":[]})", std::move(state.arena), send_json_response(conn, 200, R"({"policies":[]})", std::move(state.arena),
state.http_request_id, state.connection_close); state.http_request_id, state.connection_close);
state.reset();
} }
void HttpHandler::handle_delete_retention(Connection &conn, void HttpHandler::handle_delete_retention(Connection &conn,
HttpConnectionState &state, HttpRequestState &state,
const RouteMatch &) { const RouteMatch &) {
// TODO: Extract policy_id from URL and delete // TODO: Extract policy_id from URL and delete
send_json_response(conn, 200, R"({"policy_id":"example","status":"deleted"})", send_json_response(conn, 200, R"({"policy_id":"example","status":"deleted"})",
std::move(state.arena), state.http_request_id, std::move(state.arena), state.http_request_id,
state.connection_close); state.connection_close);
state.reset();
} }
void HttpHandler::handle_get_metrics(Connection &conn, void HttpHandler::handle_get_metrics(Connection &conn,
HttpConnectionState &state) { HttpRequestState &state) {
metrics_counter.inc(); metrics_counter.inc();
auto metrics_span = metric::render(state.arena); auto metrics_span = metric::render(state.arena);
@@ -399,8 +338,6 @@ void HttpHandler::handle_get_metrics(Connection &conn,
total_size += sv.size(); total_size += sv.size();
} }
auto *http_state = static_cast<HttpConnectionState *>(conn.user_data);
// Build HTTP response headers using arena // Build HTTP response headers using arena
std::string_view headers; std::string_view headers;
if (state.connection_close) { if (state.connection_close) {
@@ -408,15 +345,15 @@ void HttpHandler::handle_get_metrics(Connection &conn,
state.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-Type: text/plain; version=0.0.4\r\n",
"Content-Length: ", static_cast<uint64_t>(total_size), "\r\n", "Content-Length: ", static_cast<uint64_t>(total_size), "\r\n",
"X-Response-ID: ", static_cast<int64_t>(http_state->http_request_id), "X-Response-ID: ", static_cast<int64_t>(state.http_request_id), "\r\n",
"\r\n", "Connection: close\r\n", "\r\n"); "Connection: close\r\n", "\r\n");
} else { } else {
headers = static_format( headers = static_format(
state.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-Type: text/plain; version=0.0.4\r\n",
"Content-Length: ", static_cast<uint64_t>(total_size), "\r\n", "Content-Length: ", static_cast<uint64_t>(total_size), "\r\n",
"X-Response-ID: ", static_cast<int64_t>(http_state->http_request_id), "X-Response-ID: ", static_cast<int64_t>(state.http_request_id), "\r\n",
"\r\n", "Connection: keep-alive\r\n", "\r\n"); "Connection: keep-alive\r\n", "\r\n");
} }
auto result = auto result =
@@ -427,10 +364,9 @@ void HttpHandler::handle_get_metrics(Connection &conn,
*out++ = sv; *out++ = sv;
} }
conn.append_message(result, std::move(state.arena), state.connection_close); conn.append_message(result, std::move(state.arena), state.connection_close);
state.reset();
} }
void HttpHandler::handle_get_ok(Connection &, HttpConnectionState &) { void HttpHandler::handle_get_ok(Connection &, HttpRequestState &) {
ok_counter.inc(); ok_counter.inc();
TRACE_EVENT("http", "GET /ok", perfetto::Flow::Global(state.http_request_id)); TRACE_EVENT("http", "GET /ok", perfetto::Flow::Global(state.http_request_id));
@@ -438,8 +374,7 @@ void HttpHandler::handle_get_ok(Connection &, HttpConnectionState &) {
// Response will be generated in the release stage after pipeline processing // Response will be generated in the release stage after pipeline processing
} }
void HttpHandler::handle_not_found(Connection &conn, void HttpHandler::handle_not_found(Connection &conn, HttpRequestState &state) {
HttpConnectionState &state) {
send_error_response(conn, 404, "Not found", std::move(state.arena), send_error_response(conn, 404, "Not found", std::move(state.arena),
state.http_request_id, state.connection_close); state.http_request_id, state.connection_close);
} }
@@ -566,7 +501,7 @@ std::span<std::string_view> HttpHandler::format_json_response(
// llhttp callbacks // llhttp callbacks
int HttpHandler::onUrl(llhttp_t *parser, const char *at, size_t length) { int HttpHandler::onUrl(llhttp_t *parser, const char *at, size_t length) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
// Store URL in arena (simplified - would need to accumulate for streaming) // Store URL in arena (simplified - would need to accumulate for streaming)
state->url = std::string_view(at, length); state->url = std::string_view(at, length);
return 0; return 0;
@@ -574,28 +509,28 @@ int HttpHandler::onUrl(llhttp_t *parser, const char *at, size_t length) {
int HttpHandler::onHeaderField(llhttp_t *parser, const char *at, int HttpHandler::onHeaderField(llhttp_t *parser, const char *at,
size_t length) { size_t length) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
// Accumulate header field data // Accumulate header field data
state->current_header_field_buf.append(at, length); state->current_header_field_buf.append(at, length);
return 0; return 0;
} }
int HttpHandler::onHeaderFieldComplete(llhttp_t *parser) { int HttpHandler::onHeaderFieldComplete(llhttp_t *parser) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
state->header_field_complete = true; state->header_field_complete = true;
return 0; return 0;
} }
int HttpHandler::onHeaderValue(llhttp_t *parser, const char *at, int HttpHandler::onHeaderValue(llhttp_t *parser, const char *at,
size_t length) { size_t length) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
// Accumulate header value data // Accumulate header value data
state->current_header_value_buf.append(at, length); state->current_header_value_buf.append(at, length);
return 0; return 0;
} }
int HttpHandler::onHeaderValueComplete(llhttp_t *parser) { int HttpHandler::onHeaderValueComplete(llhttp_t *parser) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
if (!state->header_field_complete) { if (!state->header_field_complete) {
// Field is not complete yet, wait // Field is not complete yet, wait
@@ -634,7 +569,7 @@ int HttpHandler::onHeaderValueComplete(llhttp_t *parser) {
} }
int HttpHandler::onHeadersComplete(llhttp_t *parser) { int HttpHandler::onHeadersComplete(llhttp_t *parser) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
state->headers_complete = true; state->headers_complete = true;
// Get HTTP method // Get HTTP method
@@ -661,7 +596,7 @@ int HttpHandler::onHeadersComplete(llhttp_t *parser) {
} }
int HttpHandler::onBody(llhttp_t *parser, const char *at, size_t length) { int HttpHandler::onBody(llhttp_t *parser, const char *at, size_t length) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
if (state->parsing_commit && state->commit_parser) { if (state->parsing_commit && state->commit_parser) {
// Stream data to commit request parser // Stream data to commit request parser
@@ -676,18 +611,9 @@ int HttpHandler::onBody(llhttp_t *parser, const char *at, size_t length) {
} }
int HttpHandler::onMessageComplete(llhttp_t *parser) { int HttpHandler::onMessageComplete(llhttp_t *parser) {
auto *state = static_cast<HttpConnectionState *>(parser->data); auto *state = static_cast<HttpRequestState *>(parser->data);
state->message_complete = true; state->message_complete = true;
return HPE_PAUSED;
if (state->parsing_commit && state->commit_parser) {
// Finish streaming parse
auto status = state->commit_parser->finish_streaming_parse();
if (status == CommitRequestParser::ParseStatus::Error) {
return -1; // Signal parsing error to llhttp
}
}
return 0;
} }
// Pipeline stage implementations (batch-based) // Pipeline stage implementations (batch-based)

View File

@@ -24,10 +24,8 @@ struct RouteMatch;
* HTTP connection state stored in Connection::user_data. * HTTP connection state stored in Connection::user_data.
* Manages llhttp parser state and request data. * Manages llhttp parser state and request data.
*/ */
struct HttpConnectionState { struct HttpRequestState {
Arena arena{16 << 10}; // Request-scoped arena for parsing state Arena arena{16 << 10}; // Request-scoped arena for parsing state
llhttp_t parser;
llhttp_settings_t settings;
// Current request data (arena-allocated) // Current request data (arena-allocated)
std::string_view method; std::string_view method;
@@ -59,8 +57,17 @@ struct HttpConnectionState {
bool basic_validation_passed = bool basic_validation_passed =
false; // Set to true if basic validation passes false; // Set to true if basic validation passes
HttpRequestState();
};
struct HttpConnectionState {
llhttp_t parser;
llhttp_settings_t settings;
HttpRequestState pending;
std::deque<HttpRequestState> queue;
HttpConnectionState(); HttpConnectionState();
void reset(); // Reset state for next HTTP request (keeps arena)
}; };
/** /**
@@ -189,20 +196,20 @@ private:
bool process_release_batch(BatchType &batch); bool process_release_batch(BatchType &batch);
// Route handlers // Route handlers
void handle_get_version(Connection &conn, HttpConnectionState &state); void handle_get_version(Connection &conn, HttpRequestState &state);
void handle_post_commit(Connection &conn, HttpConnectionState &state); void handle_post_commit(Connection &conn, HttpRequestState &state);
void handle_get_subscribe(Connection &conn, HttpConnectionState &state); void handle_get_subscribe(Connection &conn, HttpRequestState &state);
void handle_get_status(Connection &conn, HttpConnectionState &state, void handle_get_status(Connection &conn, HttpRequestState &state,
const RouteMatch &route_match); const RouteMatch &route_match);
void handle_put_retention(Connection &conn, HttpConnectionState &state, void handle_put_retention(Connection &conn, HttpRequestState &state,
const RouteMatch &route_match); const RouteMatch &route_match);
void handle_get_retention(Connection &conn, HttpConnectionState &state, void handle_get_retention(Connection &conn, HttpRequestState &state,
const RouteMatch &route_match); const RouteMatch &route_match);
void handle_delete_retention(Connection &conn, HttpConnectionState &state, void handle_delete_retention(Connection &conn, HttpRequestState &state,
const RouteMatch &route_match); const RouteMatch &route_match);
void handle_get_metrics(Connection &conn, HttpConnectionState &state); void handle_get_metrics(Connection &conn, HttpRequestState &state);
void handle_get_ok(Connection &conn, HttpConnectionState &state); void handle_get_ok(Connection &conn, HttpRequestState &state);
void handle_not_found(Connection &conn, HttpConnectionState &state); void handle_not_found(Connection &conn, HttpRequestState &state);
// HTTP utilities // HTTP utilities
static void send_response(MessageSender &conn, int status_code, static void send_response(MessageSender &conn, int status_code,