mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
@@ -65,6 +65,14 @@ namespace app {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
auto constexpr HealthCheckHTML = R"html(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>Test page for Clio</title></head>
|
||||||
|
<body><h1>Clio Test</h1><p>This page shows Clio http(s) connectivity is working.</p></body>
|
||||||
|
</html>
|
||||||
|
)html";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Start context threads
|
* @brief Start context threads
|
||||||
*
|
*
|
||||||
@@ -178,6 +186,16 @@ ClioApplication::run(bool const useNgWebServer)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
httpServer->onGet(
|
||||||
|
"/health",
|
||||||
|
[](web::ng::Request const& request,
|
||||||
|
web::ng::ConnectionMetadata&,
|
||||||
|
web::SubscriptionContextPtr,
|
||||||
|
boost::asio::yield_context) -> web::ng::Response {
|
||||||
|
return web::ng::Response{boost::beast::http::status::ok, HealthCheckHTML, request};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
util::Logger webServerLog{"WebServer"};
|
util::Logger webServerLog{"WebServer"};
|
||||||
auto onRequest = [adminVerifier, &webServerLog, &handler](
|
auto onRequest = [adminVerifier, &webServerLog, &handler](
|
||||||
web::ng::Request const& request,
|
web::ng::Request const& request,
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -49,10 +48,8 @@ resolve(std::string const& ip, std::string const& port)
|
|||||||
{
|
{
|
||||||
web::Resolver resolver;
|
web::Resolver resolver;
|
||||||
|
|
||||||
if (auto const results = resolver.resolve(ip, port); not results.empty()) {
|
if (auto const results = resolver.resolve(ip, port); not results.empty())
|
||||||
std::cout << "resolved ip: '" << results.at(0) << '\n';
|
|
||||||
return results.at(0);
|
return results.at(0);
|
||||||
}
|
|
||||||
|
|
||||||
throw std::runtime_error("Failed to resolve " + ip + ":" + port);
|
throw std::runtime_error("Failed to resolve " + ip + ":" + port);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,14 @@
|
|||||||
|
|
||||||
namespace web::impl {
|
namespace web::impl {
|
||||||
|
|
||||||
|
static auto constexpr HealthCheckHTML = R"html(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>Test page for Clio</title></head>
|
||||||
|
<body><h1>Clio Test</h1><p>This page shows Clio http(s) connectivity is working.</p></body>
|
||||||
|
</html>
|
||||||
|
)html";
|
||||||
|
|
||||||
using tcp = boost::asio::ip::tcp;
|
using tcp = boost::asio::ip::tcp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -207,6 +215,9 @@ public:
|
|||||||
if (ec)
|
if (ec)
|
||||||
return httpFail(ec, "read");
|
return httpFail(ec, "read");
|
||||||
|
|
||||||
|
if (req_.method() == http::verb::get and req_.target() == "/health")
|
||||||
|
return sender_(httpResponse(http::status::ok, "text/html", HealthCheckHTML));
|
||||||
|
|
||||||
// Update isAdmin property of the connection
|
// Update isAdmin property of the connection
|
||||||
ConnectionBase::isAdmin_ = adminVerification_->isAdmin(req_, this->clientIp);
|
ConnectionBase::isAdmin_ = adminVerification_->isAdmin(req_, this->clientIp);
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include <boost/beast/http.hpp> // IWYU pragma: keep
|
#include <boost/beast/http.hpp> // IWYU pragma: keep
|
||||||
#include <boost/beast/http/field.hpp>
|
#include <boost/beast/http/field.hpp>
|
||||||
#include <boost/beast/http/message.hpp>
|
#include <boost/beast/http/message.hpp>
|
||||||
|
#include <boost/beast/http/status.hpp>
|
||||||
#include <boost/beast/http/string_body.hpp>
|
#include <boost/beast/http/string_body.hpp>
|
||||||
#include <boost/beast/http/verb.hpp>
|
#include <boost/beast/http/verb.hpp>
|
||||||
#include <boost/beast/http/write.hpp> // IWYU pragma: keep
|
#include <boost/beast/http/write.hpp> // IWYU pragma: keep
|
||||||
@@ -58,7 +59,7 @@ using tcp = boost::asio::ip::tcp;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string
|
std::pair<boost::beast::http::status, std::string>
|
||||||
syncRequest(
|
syncRequest(
|
||||||
std::string const& host,
|
std::string const& host,
|
||||||
std::string const& port,
|
std::string const& port,
|
||||||
@@ -96,7 +97,7 @@ syncRequest(
|
|||||||
boost::beast::error_code ec;
|
boost::beast::error_code ec;
|
||||||
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
|
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
|
||||||
|
|
||||||
return res.body();
|
return {res.result(), res.body()};
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -105,7 +106,7 @@ WebHeader::WebHeader(http::field name, std::string value) : name(name), value(st
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::pair<boost::beast::http::status, std::string>
|
||||||
HttpSyncClient::post(
|
HttpSyncClient::post(
|
||||||
std::string const& host,
|
std::string const& host,
|
||||||
std::string const& port,
|
std::string const& port,
|
||||||
@@ -116,7 +117,7 @@ HttpSyncClient::post(
|
|||||||
return syncRequest(host, port, body, std::move(additionalHeaders), http::verb::post);
|
return syncRequest(host, port, body, std::move(additionalHeaders), http::verb::post);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::pair<boost::beast::http::status, std::string>
|
||||||
HttpSyncClient::get(
|
HttpSyncClient::get(
|
||||||
std::string const& host,
|
std::string const& host,
|
||||||
std::string const& port,
|
std::string const& port,
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
#include <boost/beast/core/tcp_stream.hpp>
|
#include <boost/beast/core/tcp_stream.hpp>
|
||||||
#include <boost/beast/http/field.hpp>
|
#include <boost/beast/http/field.hpp>
|
||||||
#include <boost/beast/http/message.hpp>
|
#include <boost/beast/http/message.hpp>
|
||||||
|
#include <boost/beast/http/status.hpp>
|
||||||
#include <boost/beast/http/string_body.hpp>
|
#include <boost/beast/http/string_body.hpp>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct WebHeader {
|
struct WebHeader {
|
||||||
@@ -43,7 +45,7 @@ struct WebHeader {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct HttpSyncClient {
|
struct HttpSyncClient {
|
||||||
static std::string
|
static std::pair<boost::beast::http::status, std::string>
|
||||||
post(
|
post(
|
||||||
std::string const& host,
|
std::string const& host,
|
||||||
std::string const& port,
|
std::string const& port,
|
||||||
@@ -51,7 +53,7 @@ struct HttpSyncClient {
|
|||||||
std::vector<WebHeader> additionalHeaders = {}
|
std::vector<WebHeader> additionalHeaders = {}
|
||||||
);
|
);
|
||||||
|
|
||||||
static std::string
|
static std::pair<boost::beast::http::status, std::string>
|
||||||
get(std::string const& host,
|
get(std::string const& host,
|
||||||
std::string const& port,
|
std::string const& port,
|
||||||
std::string const& body,
|
std::string const& body,
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -214,8 +215,9 @@ TEST_F(WebServerTest, Http)
|
|||||||
{
|
{
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::post("localhost", port, R"({"Hello":1})");
|
auto const [status, res] = HttpSyncClient::post("localhost", port, R"({"Hello":1})");
|
||||||
EXPECT_EQ(res, R"({"Hello":1})");
|
EXPECT_EQ(res, R"({"Hello":1})");
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerTest, Ws)
|
TEST_F(WebServerTest, Ws)
|
||||||
@@ -233,11 +235,12 @@ TEST_F(WebServerTest, HttpInternalError)
|
|||||||
{
|
{
|
||||||
auto e = std::make_shared<ExceptionExecutor>();
|
auto e = std::make_shared<ExceptionExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
|
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::post("localhost", port, R"({})");
|
auto const [status, res] = HttpSyncClient::post("localhost", port, R"({})");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
res,
|
res,
|
||||||
R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})"
|
R"({"error":"internal","error_code":73,"error_message":"Internal error.","status":"error","type":"response"})"
|
||||||
);
|
);
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::internal_server_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerTest, WsInternalError)
|
TEST_F(WebServerTest, WsInternalError)
|
||||||
@@ -316,13 +319,16 @@ TEST_F(WebServerTest, HttpRequestOverload)
|
|||||||
{
|
{
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto const server = makeServerSync(cfg, ctx, dosGuardOverload, e);
|
auto const server = makeServerSync(cfg, ctx, dosGuardOverload, e);
|
||||||
auto res = HttpSyncClient::post("localhost", port, R"({})");
|
auto [status, res] = HttpSyncClient::post("localhost", port, R"({})");
|
||||||
EXPECT_EQ(res, "{}");
|
EXPECT_EQ(res, "{}");
|
||||||
res = HttpSyncClient::post("localhost", port, R"({})");
|
EXPECT_EQ(status, boost::beast::http::status::ok);
|
||||||
|
|
||||||
|
std::tie(status, res) = HttpSyncClient::post("localhost", port, R"({})");
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
res,
|
res,
|
||||||
R"({"error":"slowDown","error_code":10,"error_message":"You are placing too much load on the server.","status":"error","type":"response"})"
|
R"({"error":"slowDown","error_code":10,"error_message":"You are placing too much load on the server.","status":"error","type":"response"})"
|
||||||
);
|
);
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::service_unavailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerTest, WsRequestOverload)
|
TEST_F(WebServerTest, WsRequestOverload)
|
||||||
@@ -349,11 +355,12 @@ TEST_F(WebServerTest, HttpPayloadOverload)
|
|||||||
std::string const s100(100, 'a');
|
std::string const s100(100, 'a');
|
||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
auto server = makeServerSync(cfg, ctx, dosGuardOverload, e);
|
auto server = makeServerSync(cfg, ctx, dosGuardOverload, e);
|
||||||
auto const res = HttpSyncClient::post("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
|
auto const [status, res] = HttpSyncClient::post("localhost", port, fmt::format(R"({{"payload":"{}"}})", s100));
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
res,
|
res,
|
||||||
R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"
|
R"({"payload":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","warning":"load","warnings":[{"id":2003,"message":"You are about to be rate limited"}]})"
|
||||||
);
|
);
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerTest, WsPayloadOverload)
|
TEST_F(WebServerTest, WsPayloadOverload)
|
||||||
@@ -393,6 +400,26 @@ TEST_F(WebServerTest, WsTooManyConnection)
|
|||||||
EXPECT_TRUE(exceptionThrown);
|
EXPECT_TRUE(exceptionThrown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(WebServerTest, HealthCheck)
|
||||||
|
{
|
||||||
|
auto e = std::make_shared<ExceptionExecutor>(); // request handled before we get to executor
|
||||||
|
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
|
||||||
|
auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/health");
|
||||||
|
|
||||||
|
EXPECT_FALSE(res.empty());
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(WebServerTest, GetOtherThanHealthCheck)
|
||||||
|
{
|
||||||
|
auto e = std::make_shared<ExceptionExecutor>(); // request handled before we get to executor
|
||||||
|
auto const server = makeServerSync(cfg, ctx, dosGuard, e);
|
||||||
|
auto const [status, res] = HttpSyncClient::get("localhost", port, "", "/");
|
||||||
|
|
||||||
|
EXPECT_FALSE(res.empty());
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::bad_request);
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
JSONServerConfigWithAdminPassword(uint32_t const port)
|
JSONServerConfigWithAdminPassword(uint32_t const port)
|
||||||
{
|
{
|
||||||
@@ -500,8 +527,11 @@ TEST_P(WebServerAdminTest, HttpAdminCheck)
|
|||||||
auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e);
|
auto server = makeServerSync(serverConfig, ctx, dosGuardOverload, e);
|
||||||
std::string const request = "Why hello";
|
std::string const request = "Why hello";
|
||||||
uint32_t const webServerPort = serverConfig.value<uint32_t>("server.port");
|
uint32_t const webServerPort = serverConfig.value<uint32_t>("server.port");
|
||||||
auto const res = HttpSyncClient::post("localhost", std::to_string(webServerPort), request, GetParam().headers);
|
auto const [status, res] =
|
||||||
|
HttpSyncClient::post("localhost", std::to_string(webServerPort), request, GetParam().headers);
|
||||||
|
|
||||||
EXPECT_EQ(res, fmt::format("{} {}", request, GetParam().expectedResponse));
|
EXPECT_EQ(res, fmt::format("{} {}", request, GetParam().expectedResponse));
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(
|
INSTANTIATE_TEST_CASE_P(
|
||||||
@@ -618,8 +648,10 @@ TEST_F(WebServerPrometheusTest, rejectedWithoutAdminPassword)
|
|||||||
uint32_t const webServerPort = tests::util::generateFreePort();
|
uint32_t const webServerPort = tests::util::generateFreePort();
|
||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
|
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
|
||||||
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
|
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::get("localhost", std::to_string(webServerPort), "", "/metrics");
|
auto const [status, res] = HttpSyncClient::get("localhost", std::to_string(webServerPort), "", "/metrics");
|
||||||
|
|
||||||
EXPECT_EQ(res, "Only admin is allowed to collect metrics");
|
EXPECT_EQ(res, "Only admin is allowed to collect metrics");
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
|
TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
|
||||||
@@ -641,7 +673,7 @@ TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
|
|||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithDisabledPrometheus)};
|
Config const serverConfig{boost::json::parse(JSONServerConfigWithDisabledPrometheus)};
|
||||||
PrometheusService::init(serverConfig);
|
PrometheusService::init(serverConfig);
|
||||||
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
|
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::get(
|
auto const [status, res] = HttpSyncClient::get(
|
||||||
"localhost",
|
"localhost",
|
||||||
std::to_string(webServerPort),
|
std::to_string(webServerPort),
|
||||||
"",
|
"",
|
||||||
@@ -652,6 +684,7 @@ TEST_F(WebServerPrometheusTest, rejectedIfPrometheusIsDisabled)
|
|||||||
)}
|
)}
|
||||||
);
|
);
|
||||||
EXPECT_EQ(res, "Prometheus is disabled in clio config");
|
EXPECT_EQ(res, "Prometheus is disabled in clio config");
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::forbidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebServerPrometheusTest, validResponse)
|
TEST_F(WebServerPrometheusTest, validResponse)
|
||||||
@@ -662,7 +695,7 @@ TEST_F(WebServerPrometheusTest, validResponse)
|
|||||||
auto e = std::make_shared<EchoExecutor>();
|
auto e = std::make_shared<EchoExecutor>();
|
||||||
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
|
Config const serverConfig{boost::json::parse(JSONServerConfigWithAdminPassword(webServerPort))};
|
||||||
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
|
auto server = makeServerSync(serverConfig, ctx, dosGuard, e);
|
||||||
auto const res = HttpSyncClient::get(
|
auto const [status, res] = HttpSyncClient::get(
|
||||||
"localhost",
|
"localhost",
|
||||||
std::to_string(webServerPort),
|
std::to_string(webServerPort),
|
||||||
"",
|
"",
|
||||||
@@ -673,4 +706,5 @@ TEST_F(WebServerPrometheusTest, validResponse)
|
|||||||
)}
|
)}
|
||||||
);
|
);
|
||||||
EXPECT_EQ(res, "# TYPE test_counter counter\ntest_counter 1\n\n");
|
EXPECT_EQ(res, "# TYPE test_counter counter\ntest_counter 1\n\n");
|
||||||
|
EXPECT_EQ(status, boost::beast::http::status::ok);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user