#pragma once #include #include #include #include #include #include #include "arena_allocator.hpp" #include "connection.hpp" #include "connection_handler.hpp" #include "loop_iterations.hpp" #include "perfetto_categories.hpp" #include "pipeline_entry.hpp" #include "server.hpp" #include "thread_pipeline.hpp" // Forward declarations struct CommitRequest; struct JsonCommitRequestParser; /** * HTTP routes supported by WeaselDB server. * Using enum for efficient switch-based routing. */ enum class HttpRoute { GET_version, POST_commit, GET_subscribe, GET_status, PUT_retention, GET_retention, DELETE_retention, GET_metrics, GET_ok, NotFound }; /** * HTTP connection state stored in Connection::user_data. * Manages llhttp parser state and request data. */ struct HttpConnectionState { llhttp_t parser; llhttp_settings_t settings; // Current request data (arena-allocated) std::string_view method; std::string_view url; // Parse state bool headers_complete = false; bool message_complete = false; bool connection_close = false; // Client requested connection close HttpRoute route = HttpRoute::NotFound; // Status request data std::string_view status_request_id; // Request ID extracted from /v1/status/{id} URL // Header accumulation buffers (arena-allocated) using ArenaString = std::basic_string, ArenaStlAllocator>; ArenaString current_header_field_buf; ArenaString current_header_value_buf; bool header_field_complete = false; int64_t http_request_id = 0; // X-Request-Id header value (for tracing/logging) // Streaming parser for POST requests std::unique_ptr commit_parser; std::unique_ptr commit_request; bool parsing_commit = false; bool basic_validation_passed = false; // Set to true if basic validation passes explicit HttpConnectionState(ArenaAllocator &arena); }; /** * HTTP/1.1 server implementation using llhttp for parsing. * Supports the WeaselDB REST API endpoints with enum-based routing. */ struct HttpHandler : ConnectionHandler { HttpHandler() : banned_request_ids( ArenaStlAllocator(&banned_request_arena)) { // Stage 0: Sequence assignment thread sequenceThread = std::thread{[this]() { pthread_setname_np(pthread_self(), "txn-sequence"); for (;;) { auto guard = commitPipeline.acquire<0, 0>(); if (process_sequence_batch(guard.batch)) { return; // Shutdown signal received } } }}; // Stage 1: Precondition resolution thread resolveThread = std::thread{[this]() { pthread_setname_np(pthread_self(), "txn-resolve"); for (;;) { auto guard = commitPipeline.acquire<1, 0>(); if (process_resolve_batch(guard.batch)) { return; // Shutdown signal received } } }}; // Stage 2: Transaction persistence thread persistThread = std::thread{[this]() { pthread_setname_np(pthread_self(), "txn-persist"); for (;;) { auto guard = commitPipeline.acquire<2, 0>(); if (process_persist_batch(guard.batch)) { return; // Shutdown signal received } } }}; // Stage 3: Connection return to server thread releaseThread = std::thread{[this]() { pthread_setname_np(pthread_self(), "txn-release"); for (;;) { auto guard = commitPipeline.acquire<3, 0>(); if (process_release_batch(guard.batch)) { return; // Shutdown signal received } } }}; } ~HttpHandler() { // Send single shutdown signal that flows through all pipeline stages { auto guard = commitPipeline.push(1, true); guard.batch[0] = ShutdownEntry{}; // Single ShutdownEntry flows through all stages } // Join all pipeline threads sequenceThread.join(); resolveThread.join(); persistThread.join(); releaseThread.join(); } void on_connection_established(Connection &conn) override; void on_connection_closed(Connection &conn) override; void on_data_arrived(std::string_view data, std::unique_ptr &conn_ptr) override; void on_write_buffer_drained(std::unique_ptr &conn_ptr) override; void on_batch_complete( std::span> /*batch*/) override; // Route parsing (public for testing) static HttpRoute parseRoute(std::string_view method, std::string_view url); // llhttp callbacks (public for HttpConnectionState access) static int onUrl(llhttp_t *parser, const char *at, size_t length); static int onHeaderField(llhttp_t *parser, const char *at, size_t length); static int onHeaderFieldComplete(llhttp_t *parser); static int onHeaderValue(llhttp_t *parser, const char *at, size_t length); static int onHeaderValueComplete(llhttp_t *parser); static int onHeadersComplete(llhttp_t *parser); static int onBody(llhttp_t *parser, const char *at, size_t length); static int onMessageComplete(llhttp_t *parser); private: static constexpr int lg_size = 16; // Pipeline state (sequence thread only) int64_t next_version = 1; // Next version to assign (sequence thread only) // Pipeline state (persist thread writes, I/O threads read) std::atomic committed_version{ 0}; // Highest committed version (persist thread writes, I/O threads read) // Arena for banned request IDs and related data structures (sequence thread // only) ArenaAllocator banned_request_arena; using BannedRequestIdSet = std::unordered_set, std::equal_to, ArenaStlAllocator>; BannedRequestIdSet banned_request_ids; // Request IDs that should not commit // (string_views into arena) // Main commit processing pipeline: sequence -> resolve -> persist -> release StaticThreadPipeline commitPipeline{lg_size}; // Pipeline stage threads std::thread sequenceThread; std::thread resolveThread; std::thread persistThread; std::thread releaseThread; // Pipeline stage processing methods (batch-based) using BatchType = StaticThreadPipeline::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 handleGetVersion(Connection &conn, const HttpConnectionState &state); void handlePostCommit(Connection &conn, const HttpConnectionState &state); void handleGetSubscribe(Connection &conn, const HttpConnectionState &state); void handleGetStatus(Connection &conn, HttpConnectionState &state); void handlePutRetention(Connection &conn, const HttpConnectionState &state); void handleGetRetention(Connection &conn, const HttpConnectionState &state); void handleDeleteRetention(Connection &conn, const HttpConnectionState &state); void handleGetMetrics(Connection &conn, const HttpConnectionState &state); void handleGetOk(Connection &conn, const HttpConnectionState &state); void handleNotFound(Connection &conn, const HttpConnectionState &state); // HTTP utilities static void sendResponse(Connection &conn, int status_code, std::string_view content_type, std::string_view body, bool close_connection = false); static void send_json_response(Connection &conn, int status_code, std::string_view json, bool close_connection = false); static void send_error_response(Connection &conn, int status_code, std::string_view message, bool close_connection = false); };