#include "app/WebHandlers.hpp" #include "rpc/Errors.hpp" #include "rpc/WorkQueue.hpp" #include "util/Assert.hpp" #include "util/CoroutineGroup.hpp" #include "util/prometheus/Http.hpp" #include "web/AdminVerificationStrategy.hpp" #include "web/SubscriptionContextInterface.hpp" #include "web/dosguard/DOSGuardInterface.hpp" #include "web/ng/Connection.hpp" #include "web/ng/Request.hpp" #include "web/ng/Response.hpp" #include #include #include #include #include #include #include namespace app { OnConnectCheck::OnConnectCheck(web::dosguard::DOSGuardInterface& dosguard) : dosguard_{dosguard} { } std::expected OnConnectCheck::operator()(web::ng::Connection const& connection) { dosguard_.get().increment(connection.ip()); if (not dosguard_.get().isOk(connection.ip())) { return std::unexpected{web::ng::Response{ boost::beast::http::status::too_many_requests, "Too many requests", connection }}; } return {}; } IpChangeHook::IpChangeHook(web::dosguard::DOSGuardInterface& dosguard) : dosguard_(dosguard) { } void IpChangeHook::operator()(std::string const& oldIp, std::string const& newIp) { dosguard_.get().decrement(oldIp); dosguard_.get().increment(newIp); } DisconnectHook::DisconnectHook(web::dosguard::DOSGuardInterface& dosguard) : dosguard_{dosguard} { } void DisconnectHook::operator()(web::ng::Connection const& connection) { dosguard_.get().decrement(connection.ip()); } MetricsHandler::MetricsHandler( std::shared_ptr adminVerifier, rpc::WorkQueue& workQueue ) : adminVerifier_{std::move(adminVerifier)}, workQueue_{std::ref(workQueue)} { } web::ng::Response MetricsHandler::operator()( web::ng::Request const& request, web::ng::ConnectionMetadata& connectionMetadata, web::SubscriptionContextPtr, boost::asio::yield_context yield ) { std::optional response; util::CoroutineGroup coroutineGroup{yield, 1}; auto const onTaskComplete = coroutineGroup.registerForeign(yield); ASSERT(onTaskComplete.has_value(), "Coroutine group can't be full"); bool const postSuccessful = workQueue_.get().postCoro( [this, &request, &response, &onTaskComplete = onTaskComplete.value(), &connectionMetadata]( boost::asio::yield_context ) mutable { auto const maybeHttpRequest = request.asHttpRequest(); ASSERT(maybeHttpRequest.has_value(), "Got not a http request in Get"); auto const& httpRequest = maybeHttpRequest->get(); auto maybeResponse = util::prometheus::handlePrometheusRequest( httpRequest, adminVerifier_->isAdmin(httpRequest, connectionMetadata.ip()) ); ASSERT(maybeResponse.has_value(), "Got unexpected request for Prometheus"); response = web::ng::Response{std::move(maybeResponse).value(), request}; // notify the coroutine group that the foreign task is done onTaskComplete(); }, /* isWhiteListed= */ true, rpc::WorkQueue::Priority::High ); if (!postSuccessful) { return web::ng::Response{ boost::beast::http::status::too_many_requests, rpc::makeError(rpc::RippledError::rpcTOO_BUSY), request }; } // Put the coroutine to sleep until the foreign task is done coroutineGroup.asyncWait(yield); ASSERT(response.has_value(), "Woke up coroutine without setting response"); return std::move(response).value(); } web::ng::Response HealthCheckHandler::operator()( web::ng::Request const& request, web::ng::ConnectionMetadata&, web::SubscriptionContextPtr, boost::asio::yield_context ) { static constexpr auto kHEALTH_CHECK_HTML = R"html( Test page for Clio

Clio Test

This page shows Clio http(s) connectivity is working.

)html"; 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