From 245a808e4b33d0a80452cd99ed617731aa78348e Mon Sep 17 00:00:00 2001 From: Alex Kremer Date: Thu, 18 Sep 2025 16:20:17 +0100 Subject: [PATCH] feat: Cache state endpoint (#2642) --- src/app/ClioApplication.cpp | 3 +- src/app/WebHandlers.cpp | 30 ++++++++ src/app/WebHandlers.hpp | 32 +++++++++ src/web/HttpSession.hpp | 4 ++ src/web/Server.hpp | 17 ++++- src/web/SslHttpSession.hpp | 4 ++ src/web/impl/HttpBase.hpp | 27 ++++++++ tests/unit/app/WebHandlersTests.cpp | 27 ++++++++ tests/unit/web/ServerTests.cpp | 102 +++++++++++++++++++++------- 9 files changed, 221 insertions(+), 25 deletions(-) diff --git a/src/app/ClioApplication.cpp b/src/app/ClioApplication.cpp index 64e66099..127d7bde 100644 --- a/src/app/ClioApplication.cpp +++ b/src/app/ClioApplication.cpp @@ -189,6 +189,7 @@ ClioApplication::run(bool const useNgWebServer) httpServer->onGet("/metrics", MetricsHandler{adminVerifier}); httpServer->onGet("/health", HealthCheckHandler{}); + httpServer->onGet("/cache_state", CacheStateHandler{cache}); auto requestHandler = RequestHandler{adminVerifier, handler}; httpServer->onPost("/", requestHandler); httpServer->onWs(std::move(requestHandler)); @@ -214,7 +215,7 @@ ClioApplication::run(bool const useNgWebServer) // Init the web server auto handler = std::make_shared>(config_, backend, rpcEngine, etl, dosGuard); - auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler); + auto const httpServer = web::makeHttpServer(config_, ioc, dosGuard, handler, cache); // Blocks until stopped. // When stopped, shared_ptrs fall out of scope diff --git a/src/app/WebHandlers.cpp b/src/app/WebHandlers.cpp index 91db7dc7..b0d99385 100644 --- a/src/app/WebHandlers.cpp +++ b/src/app/WebHandlers.cpp @@ -120,4 +120,34 @@ HealthCheckHandler::operator()( return web::ng::Response{boost::beast::http::status::ok, kHEALTH_CHECK_HTML, request}; } +web::ng::Response +CacheStateHandler::operator()( + web::ng::Request const& request, + web::ng::ConnectionMetadata&, + web::SubscriptionContextPtr, + boost::asio::yield_context +) +{ + static constexpr auto kCACHE_CHECK_LOADED_HTML = R"html( + + + Cache state +

Cache state

Cache is fully loaded

+ +)html"; + + static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html( + + + Cache state +

Cache state

Cache is not yet loaded

+ +)html"; + + if (cache_.get().isFull()) + return web::ng::Response{boost::beast::http::status::ok, kCACHE_CHECK_LOADED_HTML, request}; + + return web::ng::Response{boost::beast::http::status::service_unavailable, kCACHE_CHECK_NOT_LOADED_HTML, request}; +} + } // namespace app diff --git a/src/app/WebHandlers.hpp b/src/app/WebHandlers.hpp index ecd7f665..a70d0fca 100644 --- a/src/app/WebHandlers.hpp +++ b/src/app/WebHandlers.hpp @@ -19,6 +19,7 @@ #pragma once +#include "data/LedgerCacheInterface.hpp" #include "rpc/Errors.hpp" #include "util/log/Logger.hpp" #include "web/AdminVerificationStrategy.hpp" @@ -163,6 +164,37 @@ public: ); }; +/** + * @brief A function object that handles the cache state check endpoint. + */ +class CacheStateHandler { + std::reference_wrapper cache_; + +public: + /** + * @brief Construct a new CacheStateHandler object. + * + * @param cache The ledger cache to use. + */ + CacheStateHandler(data::LedgerCacheInterface const& cache) : cache_{cache} + { + } + + /** + * @brief The call of the function object. + * + * @param request The request to handle. + * @return The response to the request + */ + web::ng::Response + operator()( + web::ng::Request const& request, + web::ng::ConnectionMetadata&, + web::SubscriptionContextPtr, + boost::asio::yield_context + ); +}; + /** * @brief A function object that handles the websocket endpoint. * diff --git a/src/web/HttpSession.hpp b/src/web/HttpSession.hpp index 22171126..7f1bab9f 100644 --- a/src/web/HttpSession.hpp +++ b/src/web/HttpSession.hpp @@ -19,6 +19,7 @@ #pragma once +#include "data/LedgerCacheInterface.hpp" #include "util/Taggable.hpp" #include "web/AdminVerificationStrategy.hpp" #include "web/PlainWsSession.hpp" @@ -69,6 +70,7 @@ public: * @param tagFactory A factory that is used to generate tags to track requests and sessions * @param dosGuard The denial of service guard to use * @param handler The server handler to use + * @param cache The ledger cache to use * @param buffer Buffer with initial data received from the peer * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket */ @@ -80,6 +82,7 @@ public: std::reference_wrapper tagFactory, std::reference_wrapper dosGuard, std::shared_ptr const& handler, + std::reference_wrapper cache, boost::beast::flat_buffer buffer, std::uint32_t maxWsSendingQueueSize ) @@ -90,6 +93,7 @@ public: std::move(proxyIpResolver), dosGuard, handler, + cache, std::move(buffer) ) , stream_(std::move(socket)) diff --git a/src/web/Server.hpp b/src/web/Server.hpp index f366cd5c..e245eea0 100644 --- a/src/web/Server.hpp +++ b/src/web/Server.hpp @@ -19,6 +19,7 @@ #pragma once +#include "data/LedgerCacheInterface.hpp" #include "util/Taggable.hpp" #include "util/log/Logger.hpp" #include "web/AdminVerificationStrategy.hpp" @@ -85,6 +86,7 @@ class Detector : public std::enable_shared_from_this tagFactory_; std::reference_wrapper const dosGuard_; std::shared_ptr const handler_; + std::reference_wrapper cache_; boost::beast::flat_buffer buffer_; std::shared_ptr const adminVerification_; std::uint32_t maxWsSendingQueueSize_; @@ -99,6 +101,7 @@ public: * @param tagFactory A factory that is used to generate tags to track requests and sessions * @param dosGuard The denial of service guard to use * @param handler The server handler to use + * @param cache The ledger cache to use * @param adminVerification The admin verification strategy to use * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket * @param proxyIpResolver The client ip resolver if a request was forwarded by a proxy @@ -109,6 +112,7 @@ public: std::reference_wrapper tagFactory, std::reference_wrapper dosGuard, std::shared_ptr handler, + std::reference_wrapper cache, std::shared_ptr adminVerification, std::uint32_t maxWsSendingQueueSize, std::shared_ptr proxyIpResolver @@ -118,6 +122,7 @@ public: , tagFactory_(std::cref(tagFactory)) , dosGuard_(dosGuard) , handler_(std::move(handler)) + , cache_(cache) , adminVerification_(std::move(adminVerification)) , maxWsSendingQueueSize_(maxWsSendingQueueSize) , proxyIpResolver_(std::move(proxyIpResolver)) @@ -179,6 +184,7 @@ public: tagFactory_, dosGuard_, handler_, + cache_, std::move(buffer_), maxWsSendingQueueSize_ ) @@ -194,6 +200,7 @@ public: tagFactory_, dosGuard_, handler_, + cache_, std::move(buffer_), maxWsSendingQueueSize_ ) @@ -223,6 +230,7 @@ class Server : public std::enable_shared_from_this dosGuard_; std::shared_ptr handler_; + std::reference_wrapper cache_; tcp::acceptor acceptor_; std::shared_ptr adminVerification_; std::uint32_t maxWsSendingQueueSize_; @@ -238,6 +246,7 @@ public: * @param tagFactory A factory that is used to generate tags to track requests and sessions * @param dosGuard The denial of service guard to use * @param handler The server handler to use + * @param cache The ledger cache to use * @param adminVerification The admin verification strategy to use * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket * @param proxyIpResolver The client ip resolver if a request was forwarded by a proxy @@ -249,6 +258,7 @@ public: util::TagDecoratorFactory tagFactory, dosguard::DOSGuardInterface& dosGuard, std::shared_ptr handler, + std::reference_wrapper cache, std::shared_ptr adminVerification, std::uint32_t maxWsSendingQueueSize, ProxyIpResolver proxyIpResolver @@ -258,6 +268,7 @@ public: , tagFactory_(tagFactory) , dosGuard_(std::ref(dosGuard)) , handler_(std::move(handler)) + , cache_(cache) , acceptor_(boost::asio::make_strand(ioc)) , adminVerification_(std::move(adminVerification)) , maxWsSendingQueueSize_(maxWsSendingQueueSize) @@ -320,6 +331,7 @@ private: std::cref(tagFactory_), dosGuard_, handler_, + cache_, adminVerification_, maxWsSendingQueueSize_, proxyIpResolver_ @@ -343,6 +355,7 @@ using HttpServer = Server; * @param ioc The server will run under this io_context * @param dosGuard The dos guard to protect the server * @param handler The handler to process the request + * @param cache The ledger cache to use * @return The server instance */ template @@ -351,7 +364,8 @@ makeHttpServer( util::config::ClioConfigDefinition const& config, boost::asio::io_context& ioc, dosguard::DOSGuardInterface& dosGuard, - std::shared_ptr const& handler + std::shared_ptr const& handler, + std::reference_wrapper cache ) { static util::Logger const log{"WebServer"}; // NOLINT(readability-identifier-naming) @@ -385,6 +399,7 @@ makeHttpServer( util::TagDecoratorFactory(config), dosGuard, handler, + cache, std::move(expectedAdminVerification).value(), maxWsSendingQueueSize, std::move(proxyIpResolver) diff --git a/src/web/SslHttpSession.hpp b/src/web/SslHttpSession.hpp index 51d36139..41a6da20 100644 --- a/src/web/SslHttpSession.hpp +++ b/src/web/SslHttpSession.hpp @@ -19,6 +19,7 @@ #pragma once +#include "data/LedgerCacheInterface.hpp" #include "util/Taggable.hpp" #include "web/AdminVerificationStrategy.hpp" #include "web/ProxyIpResolver.hpp" @@ -76,6 +77,7 @@ public: * @param tagFactory A factory that is used to generate tags to track requests and sessions * @param dosGuard The denial of service guard to use * @param handler The server handler to use + * @param cache The ledger cache to use * @param buffer Buffer with initial data received from the peer * @param maxWsSendingQueueSize The maximum size of the sending queue for websocket */ @@ -88,6 +90,7 @@ public: std::reference_wrapper tagFactory, std::reference_wrapper dosGuard, std::shared_ptr const& handler, + std::reference_wrapper cache, boost::beast::flat_buffer buffer, std::uint32_t maxWsSendingQueueSize ) @@ -98,6 +101,7 @@ public: std::move(proxyIpResolver), dosGuard, handler, + cache, std::move(buffer) ) , stream_(std::move(socket), ctx) diff --git a/src/web/impl/HttpBase.hpp b/src/web/impl/HttpBase.hpp index 4dd90f42..aed5d7e9 100644 --- a/src/web/impl/HttpBase.hpp +++ b/src/web/impl/HttpBase.hpp @@ -19,6 +19,7 @@ #pragma once +#include "data/LedgerCacheInterface.hpp" #include "rpc/Errors.hpp" #include "util/Assert.hpp" #include "util/Taggable.hpp" @@ -71,6 +72,22 @@ static constexpr auto kHEALTH_CHECK_HTML = R"html( )html"; +static constexpr auto kCACHE_CHECK_LOADED_HTML = R"html( + + + Cache state +

Cache state

Cache is fully loaded

+ +)html"; + +static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html( + + + Cache state +

Cache state

Cache is not yet loaded

+ +)html"; + using tcp = boost::asio::ip::tcp; /** @@ -128,6 +145,7 @@ protected: http::request req_; std::reference_wrapper dosGuard_; std::shared_ptr const handler_; + std::reference_wrapper cache_; util::Logger log_{"WebServer"}; util::Logger perfLog_{"Performance"}; @@ -169,6 +187,7 @@ public: std::shared_ptr proxyIpResolver, std::reference_wrapper dosGuard, std::shared_ptr handler, + std::reference_wrapper cache, boost::beast::flat_buffer buffer ) : ConnectionBase(tagFactory, ip) @@ -178,6 +197,7 @@ public: , buffer_(std::move(buffer)) , dosGuard_(dosGuard) , handler_(std::move(handler)) + , cache_(cache) { LOG(perfLog_.debug()) << tag() << "http session created"; dosGuard_.get().increment(ip); @@ -222,6 +242,13 @@ public: if (req_.method() == http::verb::get and req_.target() == "/health") return sender_(httpResponse(http::status::ok, "text/html", kHEALTH_CHECK_HTML)); + if (req_.method() == http::verb::get and req_.target() == "/cache_state") { + if (cache_.get().isFull()) + return sender_(httpResponse(http::status::ok, "text/html", kCACHE_CHECK_LOADED_HTML)); + + return sender_(httpResponse(http::status::service_unavailable, "text/html", kCACHE_CHECK_NOT_LOADED_HTML)); + } + if (auto resolvedIp = proxyIpResolver_->resolveClientIp(clientIp_, req_); resolvedIp != clientIp_) { LOG(log_.info()) << tag() << "Detected a forwarded request from proxy. Proxy ip: " << clientIp_ << ". Resolved client ip: " << resolvedIp; diff --git a/tests/unit/app/WebHandlersTests.cpp b/tests/unit/app/WebHandlersTests.cpp index d729f702..c2177e9f 100644 --- a/tests/unit/app/WebHandlersTests.cpp +++ b/tests/unit/app/WebHandlersTests.cpp @@ -20,6 +20,7 @@ #include "app/WebHandlers.hpp" #include "rpc/Errors.hpp" #include "util/AsioContextTestFixture.hpp" +#include "util/MockLedgerCache.hpp" #include "util/MockPrometheus.hpp" #include "util/Taggable.hpp" #include "util/config/ConfigDefinition.hpp" @@ -149,6 +150,32 @@ TEST_F(HealthCheckHandlerTests, Call) }); } +struct CacheStateHandlerTests : SyncAsioContextTest, WebHandlersTest { + web::ng::Request request{http::request{http::verb::get, "/", 11}}; + MockLedgerCache cache; + CacheStateHandler cacheStateHandler{cache}; +}; + +TEST_F(CacheStateHandlerTests, CallWithCacheLoaded) +{ + EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(true)); + runSpawn([&](boost::asio::yield_context yield) { + auto response = cacheStateHandler(request, connectionMock, nullptr, yield); + auto const httpResponse = std::move(response).intoHttpResponse(); + EXPECT_EQ(httpResponse.result(), boost::beast::http::status::ok); + }); +} + +TEST_F(CacheStateHandlerTests, CallWithoutCacheLoaded) +{ + EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(false)); + runSpawn([&](boost::asio::yield_context yield) { + auto response = cacheStateHandler(request, connectionMock, nullptr, yield); + auto const httpResponse = std::move(response).intoHttpResponse(); + EXPECT_EQ(httpResponse.result(), boost::beast::http::status::service_unavailable); + }); +} + struct RequestHandlerTest : SyncAsioContextTest, WebHandlersTest { AdminVerificationStrategyStrictMockPtr adminVerifier{ std::make_shared>() diff --git a/tests/unit/web/ServerTests.cpp b/tests/unit/web/ServerTests.cpp index 31ad58a9..4c588322 100644 --- a/tests/unit/web/ServerTests.cpp +++ b/tests/unit/web/ServerTests.cpp @@ -17,7 +17,9 @@ */ //============================================================================== +#include "data/LedgerCacheInterface.hpp" #include "util/AssignRandomPort.hpp" +#include "util/MockLedgerCache.hpp" #include "util/MockPrometheus.hpp" #include "util/TestHttpClient.hpp" #include "util/TestWebSocketClient.hpp" @@ -48,11 +50,13 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -223,15 +227,18 @@ makeServerSync( util::config::ClioConfigDefinition const& config, boost::asio::io_context& ioc, web::dosguard::DOSGuardInterface& dosGuard, - std::shared_ptr const& handler + std::shared_ptr const& handler, + std::reference_wrapper cache ) { auto server = std::shared_ptr>(); + std::mutex m; std::condition_variable cv; bool ready = false; + boost::asio::dispatch(ioc.get_executor(), [&]() mutable { - server = web::makeHttpServer(config, ioc, dosGuard, handler); + server = web::makeHttpServer(config, ioc, dosGuard, handler, cache); { std::lock_guard const lk(m); ready = true; @@ -249,8 +256,9 @@ makeServerSync( TEST_F(WebServerTest, Http) { + auto cache = MockLedgerCache(); auto const e = std::make_shared(); - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); auto const [status, res] = HttpSyncClient::post("localhost", port, R"JSON({"Hello":1})JSON"); EXPECT_EQ(res, R"JSON({"Hello":1})JSON"); EXPECT_EQ(status, boost::beast::http::status::ok); @@ -258,8 +266,9 @@ TEST_F(WebServerTest, Http) TEST_F(WebServerTest, Ws) { + auto cache = MockLedgerCache(); auto e = std::make_shared(); - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); WebSocketSyncClient wsClient; wsClient.connect("localhost", port); auto const res = wsClient.syncPost(R"JSON({"Hello":1})JSON"); @@ -269,8 +278,9 @@ TEST_F(WebServerTest, Ws) TEST_F(WebServerTest, HttpInternalError) { + auto cache = MockLedgerCache(); auto const e = std::make_shared(); - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); auto const [status, res] = HttpSyncClient::post("localhost", port, R"JSON({})JSON"); EXPECT_EQ( res, @@ -281,8 +291,9 @@ TEST_F(WebServerTest, HttpInternalError) TEST_F(WebServerTest, WsInternalError) { + auto cache = MockLedgerCache(); auto e = std::make_shared(); - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); WebSocketSyncClient wsClient; wsClient.connect("localhost", port); auto const res = wsClient.syncPost(R"JSON({"id":"id1"})JSON"); @@ -295,8 +306,9 @@ TEST_F(WebServerTest, WsInternalError) TEST_F(WebServerTest, WsInternalErrorNotJson) { + auto cache = MockLedgerCache(); auto e = std::make_shared(); - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); WebSocketSyncClient wsClient; wsClient.connect("localhost", port); auto const res = wsClient.syncPost("not json"); @@ -314,7 +326,8 @@ TEST_F(WebServerTest, IncompleteSslConfig) auto jsonConfig = generateJSONWithDynamicPort(port); jsonConfig.as_object()["ssl_key_file"] = sslKeyFile.path; - auto const server = makeServerSync(getParseServerConfig(jsonConfig), ctx, dosGuard, e); + auto cache = MockLedgerCache(); + auto const server = makeServerSync(getParseServerConfig(jsonConfig), ctx, dosGuard, e, cache); EXPECT_EQ(server, nullptr); } @@ -326,24 +339,27 @@ TEST_F(WebServerTest, WrongSslConfig) jsonConfig.as_object()["ssl_key_file"] = sslKeyFile.path; jsonConfig.as_object()["ssl_cert_file"] = "wrong_path"; - auto const server = makeServerSync(getParseServerConfig(jsonConfig), ctx, dosGuard, e); + auto cache = MockLedgerCache(); + auto const server = makeServerSync(getParseServerConfig(jsonConfig), ctx, dosGuard, e, cache); EXPECT_EQ(server, nullptr); } TEST_F(WebServerTest, Https) { + auto cache = MockLedgerCache(); auto const e = std::make_shared(); cfg = getParseServerConfig(addSslConfig(generateJSONWithDynamicPort(port))); - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); auto const res = HttpsSyncClient::syncPost("localhost", port, R"JSON({"Hello":1})JSON"); EXPECT_EQ(res, R"JSON({"Hello":1})JSON"); } TEST_F(WebServerTest, Wss) { + auto cache = MockLedgerCache(); auto e = std::make_shared(); cfg = getParseServerConfig(addSslConfig(generateJSONWithDynamicPort(port))); - auto server = makeServerSync(cfg, ctx, dosGuard, e); + auto server = makeServerSync(cfg, ctx, dosGuard, e, cache); WebServerSslSyncClient wsClient; wsClient.connect("localhost", port); auto const res = wsClient.syncPost(R"JSON({"Hello":1})JSON"); @@ -354,8 +370,9 @@ TEST_F(WebServerTest, Wss) TEST_F(WebServerTest, HttpPayloadOverload) { std::string const s100(100, 'a'); + auto cache = MockLedgerCache(); auto const e = std::make_shared(); - auto server = makeServerSync(cfg, ctx, dosGuardOverload, e); + auto server = makeServerSync(cfg, ctx, dosGuardOverload, e, cache); auto const [status, res] = HttpSyncClient::post("localhost", port, fmt::format(R"JSON({{"payload":"{}"}})JSON", s100)); EXPECT_EQ( @@ -368,8 +385,9 @@ TEST_F(WebServerTest, HttpPayloadOverload) TEST_F(WebServerTest, WsPayloadOverload) { std::string const s100(100, 'a'); + auto cache = MockLedgerCache(); auto const e = std::make_shared(); - auto server = makeServerSync(cfg, ctx, dosGuardOverload, e); + auto server = makeServerSync(cfg, ctx, dosGuardOverload, e, cache); WebSocketSyncClient wsClient; wsClient.connect("localhost", port); auto const res = wsClient.syncPost(fmt::format(R"JSON({{"payload":"{}"}})JSON", s100)); @@ -382,8 +400,9 @@ TEST_F(WebServerTest, WsPayloadOverload) TEST_F(WebServerTest, WsTooManyConnection) { + auto cache = MockLedgerCache(); auto const e = std::make_shared(); - auto server = makeServerSync(cfg, ctx, dosGuardOverload, e); + auto server = makeServerSync(cfg, ctx, dosGuardOverload, e, cache); // max connection is 2, exception should happen when the third connection is made WebSocketSyncClient wsClient1; wsClient1.connect("localhost", port); @@ -404,18 +423,46 @@ TEST_F(WebServerTest, WsTooManyConnection) TEST_F(WebServerTest, HealthCheck) { + auto cache = MockLedgerCache(); auto e = std::make_shared(); // request handled before we get to executor - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/health"); EXPECT_FALSE(res.empty()); EXPECT_EQ(status, boost::beast::http::status::ok); } +TEST_F(WebServerTest, CacheStateCheckWithLoadedCache) +{ + auto cache = MockLedgerCache(); + EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(true)); + + auto e = std::make_shared(); // request handled before we get to executor + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); + auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/cache_state"); + + EXPECT_FALSE(res.empty()); + EXPECT_EQ(status, boost::beast::http::status::ok); +} + +TEST_F(WebServerTest, CacheStateCheckWithoutLoadedCache) +{ + auto cache = MockLedgerCache(); + EXPECT_CALL(cache, isFull()).WillRepeatedly(testing::Return(false)); + + auto e = std::make_shared(); // request handled before we get to executor + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); + auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/cache_state"); + + EXPECT_FALSE(res.empty()); + EXPECT_EQ(status, boost::beast::http::status::service_unavailable); +} + TEST_F(WebServerTest, GetOtherThanHealthCheck) { + auto cache = MockLedgerCache(); auto e = std::make_shared(); // request handled before we get to executor - auto const server = makeServerSync(cfg, ctx, dosGuard, e); + auto const server = makeServerSync(cfg, ctx, dosGuard, e, cache); auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/"); EXPECT_FALSE(res.empty()); @@ -539,9 +586,10 @@ class WebServerAdminTest : public WebServerTest, public ::testing::WithParamInte TEST_P(WebServerAdminTest, WsAdminCheck) { + auto cache = MockLedgerCache(); auto e = std::make_shared(); ClioConfigDefinition const serverConfig{getParseAdminServerConfig(boost::json::parse(GetParam().config))}; - auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e); + auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e, cache); WebSocketSyncClient wsClient; uint32_t const webServerPort = serverConfig.get("server.port"); wsClient.connect("localhost", std::to_string(webServerPort), GetParam().headers); @@ -553,9 +601,10 @@ TEST_P(WebServerAdminTest, WsAdminCheck) TEST_P(WebServerAdminTest, HttpAdminCheck) { + auto cache = MockLedgerCache(); auto const e = std::make_shared(); ClioConfigDefinition const serverConfig{getParseAdminServerConfig(boost::json::parse(GetParam().config))}; - auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e); + auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e, cache); std::string const request = "Why hello"; uint32_t const webServerPort = serverConfig.get("server.port"); auto const [status, res] = @@ -651,7 +700,9 @@ TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminSet) ClioConfigDefinition const serverConfig{ getParseAdminServerConfig(boost::json::parse(jsonServerConfigWithBothAdminPasswordAndLocalAdmin)) }; - EXPECT_THROW(web::makeHttpServer(serverConfig, ctx, dosGuardOverload, e), std::logic_error); + + MockLedgerCache cache; + EXPECT_THROW(web::makeHttpServer(serverConfig, ctx, dosGuardOverload, e, cache), std::logic_error); } TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminFalse) @@ -672,19 +723,22 @@ TEST_F(WebServerTest, AdminErrorCfgTestBothAdminPasswordAndLocalAdminFalse) ClioConfigDefinition const serverConfig{ getParseAdminServerConfig(boost::json::parse(jsonServerConfigWithNoAdminPasswordAndLocalAdminFalse)) }; - EXPECT_THROW(web::makeHttpServer(serverConfig, ctx, dosGuardOverload, e), std::logic_error); + + MockLedgerCache cache; + EXPECT_THROW(web::makeHttpServer(serverConfig, ctx, dosGuardOverload, e, cache), std::logic_error); } struct WebServerPrometheusTest : util::prometheus::WithPrometheus, WebServerTest {}; TEST_F(WebServerPrometheusTest, rejectedWithoutAdminPassword) { + auto cache = MockLedgerCache(); auto const e = std::make_shared(); uint32_t const webServerPort = tests::util::generateFreePort(); ClioConfigDefinition const serverConfig{ getParseAdminServerConfig(boost::json::parse(jsonServerConfigWithAdminPassword(webServerPort))) }; - auto server = makeServerSync(serverConfig, ctx, dosGuard, e); + auto server = makeServerSync(serverConfig, ctx, dosGuard, e, cache); auto const [status, res] = HttpSyncClient::get("localhost", std::to_string(webServerPort), "", "/metrics"); EXPECT_EQ(res, "Only admin is allowed to collect metrics"); @@ -708,11 +762,12 @@ TEST_F(WebServerPrometheusDisabledTest, rejectedIfPrometheusIsDisabled) webServerPort ); + auto cache = MockLedgerCache(); auto const e = std::make_shared(); ClioConfigDefinition const serverConfig{ getParseAdminServerConfig(boost::json::parse(jsonServerConfigWithDisabledPrometheus)) }; - auto server = makeServerSync(serverConfig, ctx, dosGuard, e); + auto server = makeServerSync(serverConfig, ctx, dosGuard, e, cache); auto const [status, res] = HttpSyncClient::get( "localhost", std::to_string(webServerPort), @@ -729,6 +784,7 @@ TEST_F(WebServerPrometheusDisabledTest, rejectedIfPrometheusIsDisabled) TEST_F(WebServerPrometheusTest, validResponse) { + auto cache = MockLedgerCache(); uint32_t const webServerPort = tests::util::generateFreePort(); auto& testCounter = PrometheusService::counterInt("test_counter", util::prometheus::Labels()); ++testCounter; @@ -736,7 +792,7 @@ TEST_F(WebServerPrometheusTest, validResponse) ClioConfigDefinition const serverConfig{ getParseAdminServerConfig(boost::json::parse(jsonServerConfigWithAdminPassword(webServerPort))) }; - auto server = makeServerSync(serverConfig, ctx, dosGuard, e); + auto server = makeServerSync(serverConfig, ctx, dosGuard, e, cache); auto const [status, res] = HttpSyncClient::get( "localhost", std::to_string(webServerPort),