mirror of
				https://github.com/XRPLF/clio.git
				synced 2025-11-04 11:55:51 +00:00 
			
		
		
		
	feat: Cache state endpoint (#2642)
This commit is contained in:
		@@ -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<web::RPCServerHandler<RPCEngineType>>(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
 | 
			
		||||
 
 | 
			
		||||
@@ -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(
 | 
			
		||||
    <!DOCTYPE html>
 | 
			
		||||
    <html>
 | 
			
		||||
        <head><title>Cache state</title></head>
 | 
			
		||||
        <body><h1>Cache state</h1><p>Cache is fully loaded</p></body>
 | 
			
		||||
    </html>
 | 
			
		||||
)html";
 | 
			
		||||
 | 
			
		||||
    static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html(
 | 
			
		||||
    <!DOCTYPE html>
 | 
			
		||||
    <html>
 | 
			
		||||
        <head><title>Cache state</title></head>
 | 
			
		||||
        <body><h1>Cache state</h1><p>Cache is not yet loaded</p></body>
 | 
			
		||||
    </html>
 | 
			
		||||
)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
 | 
			
		||||
 
 | 
			
		||||
@@ -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<data::LedgerCacheInterface const> 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.
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
@@ -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<util::TagDecoratorFactory const> tagFactory,
 | 
			
		||||
        std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
 | 
			
		||||
        std::shared_ptr<HandlerType> const& handler,
 | 
			
		||||
        std::reference_wrapper<data::LedgerCacheInterface const> 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))
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Detector<PlainSessionType,
 | 
			
		||||
    std::reference_wrapper<util::TagDecoratorFactory const> tagFactory_;
 | 
			
		||||
    std::reference_wrapper<dosguard::DOSGuardInterface> const dosGuard_;
 | 
			
		||||
    std::shared_ptr<HandlerType> const handler_;
 | 
			
		||||
    std::reference_wrapper<data::LedgerCacheInterface const> cache_;
 | 
			
		||||
    boost::beast::flat_buffer buffer_;
 | 
			
		||||
    std::shared_ptr<AdminVerificationStrategy> 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<util::TagDecoratorFactory const> tagFactory,
 | 
			
		||||
        std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
 | 
			
		||||
        std::shared_ptr<HandlerType> handler,
 | 
			
		||||
        std::reference_wrapper<data::LedgerCacheInterface const> cache,
 | 
			
		||||
        std::shared_ptr<AdminVerificationStrategy> adminVerification,
 | 
			
		||||
        std::uint32_t maxWsSendingQueueSize,
 | 
			
		||||
        std::shared_ptr<ProxyIpResolver> 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<Server<PlainSessionType, SslS
 | 
			
		||||
    util::TagDecoratorFactory tagFactory_;
 | 
			
		||||
    std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
 | 
			
		||||
    std::shared_ptr<HandlerType> handler_;
 | 
			
		||||
    std::reference_wrapper<data::LedgerCacheInterface const> cache_;
 | 
			
		||||
    tcp::acceptor acceptor_;
 | 
			
		||||
    std::shared_ptr<AdminVerificationStrategy> 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<HandlerType> handler,
 | 
			
		||||
        std::reference_wrapper<data::LedgerCacheInterface const> cache,
 | 
			
		||||
        std::shared_ptr<AdminVerificationStrategy> 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<HttpSession, SslHttpSession, HandlerType>;
 | 
			
		||||
 * @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 <typename HandlerType>
 | 
			
		||||
@@ -351,7 +364,8 @@ makeHttpServer(
 | 
			
		||||
    util::config::ClioConfigDefinition const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    dosguard::DOSGuardInterface& dosGuard,
 | 
			
		||||
    std::shared_ptr<HandlerType> const& handler
 | 
			
		||||
    std::shared_ptr<HandlerType> const& handler,
 | 
			
		||||
    std::reference_wrapper<data::LedgerCacheInterface const> 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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<util::TagDecoratorFactory const> tagFactory,
 | 
			
		||||
        std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
 | 
			
		||||
        std::shared_ptr<HandlerType> const& handler,
 | 
			
		||||
        std::reference_wrapper<data::LedgerCacheInterface const> 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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
)html";
 | 
			
		||||
 | 
			
		||||
static constexpr auto kCACHE_CHECK_LOADED_HTML = R"html(
 | 
			
		||||
    <!DOCTYPE html>
 | 
			
		||||
    <html>
 | 
			
		||||
        <head><title>Cache state</title></head>
 | 
			
		||||
        <body><h1>Cache state</h1><p>Cache is fully loaded</p></body>
 | 
			
		||||
    </html>
 | 
			
		||||
)html";
 | 
			
		||||
 | 
			
		||||
static constexpr auto kCACHE_CHECK_NOT_LOADED_HTML = R"html(
 | 
			
		||||
    <!DOCTYPE html>
 | 
			
		||||
    <html>
 | 
			
		||||
        <head><title>Cache state</title></head>
 | 
			
		||||
        <body><h1>Cache state</h1><p>Cache is not yet loaded</p></body>
 | 
			
		||||
    </html>
 | 
			
		||||
)html";
 | 
			
		||||
 | 
			
		||||
using tcp = boost::asio::ip::tcp;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -128,6 +145,7 @@ protected:
 | 
			
		||||
    http::request<http::string_body> req_;
 | 
			
		||||
    std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
 | 
			
		||||
    std::shared_ptr<HandlerType> const handler_;
 | 
			
		||||
    std::reference_wrapper<data::LedgerCacheInterface const> cache_;
 | 
			
		||||
    util::Logger log_{"WebServer"};
 | 
			
		||||
    util::Logger perfLog_{"Performance"};
 | 
			
		||||
 | 
			
		||||
@@ -169,6 +187,7 @@ public:
 | 
			
		||||
        std::shared_ptr<ProxyIpResolver> proxyIpResolver,
 | 
			
		||||
        std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
 | 
			
		||||
        std::shared_ptr<HandlerType> handler,
 | 
			
		||||
        std::reference_wrapper<data::LedgerCacheInterface const> 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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::string_body>{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<testing::StrictMock<AdminVerificationStrategyMock>>()
 | 
			
		||||
 
 | 
			
		||||
@@ -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 <boost/json/value.hpp>
 | 
			
		||||
#include <boost/system/system_error.hpp>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <gmock/gmock.h>
 | 
			
		||||
#include <gtest/gtest.h>
 | 
			
		||||
#include <test_data/SslCert.hpp>
 | 
			
		||||
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <optional>
 | 
			
		||||
@@ -223,15 +227,18 @@ makeServerSync(
 | 
			
		||||
    util::config::ClioConfigDefinition const& config,
 | 
			
		||||
    boost::asio::io_context& ioc,
 | 
			
		||||
    web::dosguard::DOSGuardInterface& dosGuard,
 | 
			
		||||
    std::shared_ptr<Executor> const& handler
 | 
			
		||||
    std::shared_ptr<Executor> const& handler,
 | 
			
		||||
    std::reference_wrapper<data::LedgerCacheInterface const> cache
 | 
			
		||||
)
 | 
			
		||||
{
 | 
			
		||||
    auto server = std::shared_ptr<web::HttpServer<Executor>>();
 | 
			
		||||
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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<ExceptionExecutor>();
 | 
			
		||||
    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<ExceptionExecutor>();
 | 
			
		||||
    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<ExceptionExecutor>();
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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<ExceptionExecutor>();  // 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<ExceptionExecutor>();  // 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<ExceptionExecutor>();  // 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<ExceptionExecutor>();  // 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<AdminCheckExecutor>();
 | 
			
		||||
    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<uint32_t>("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<AdminCheckExecutor>();
 | 
			
		||||
    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<uint32_t>("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<EchoExecutor>();
 | 
			
		||||
    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<EchoExecutor>();
 | 
			
		||||
    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),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user