mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-03 18:15:51 +00:00
feat: Dosguard API weights (#2082)
Experimental support for Dosguard API weights.
This commit is contained in:
@@ -29,11 +29,13 @@
|
||||
#include "util/newconfig/ConfigValue.hpp"
|
||||
#include "util/newconfig/Types.hpp"
|
||||
#include "web/SubscriptionContextInterface.hpp"
|
||||
#include "web/dosguard/DOSGuardMock.hpp"
|
||||
#include "web/ng/MockConnection.hpp"
|
||||
#include "web/ng/RPCServerHandler.hpp"
|
||||
#include "web/ng/Request.hpp"
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/beast/core/buffers_to_string.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
@@ -72,23 +74,84 @@ protected:
|
||||
std::shared_ptr<testing::StrictMock<MockRPCEngine>> rpcEngine_ =
|
||||
std::make_shared<testing::StrictMock<MockRPCEngine>>();
|
||||
std::shared_ptr<StrictMock<MockETLService>> etl_ = std::make_shared<StrictMock<MockETLService>>();
|
||||
RPCServerHandler<MockRPCEngine> rpcServerHandler_{config, backend_, rpcEngine_, etl_};
|
||||
DOSGuardStrictMock dosguard_;
|
||||
RPCServerHandler<MockRPCEngine> rpcServerHandler_{config, backend_, rpcEngine_, etl_, dosguard_};
|
||||
|
||||
util::TagDecoratorFactory tagFactory_{config};
|
||||
StrictMockConnectionMetadata connectionMetadata_{"some ip", tagFactory_};
|
||||
std::string const ip_ = "some ip";
|
||||
StrictMockConnectionMetadata connectionMetadata_{ip_, tagFactory_};
|
||||
Request::HttpHeaders const httpHeaders_;
|
||||
|
||||
static Request
|
||||
makeHttpRequest(std::string_view body)
|
||||
{
|
||||
return Request{http::request<http::string_body>{http::verb::post, "/", 11, body}};
|
||||
}
|
||||
|
||||
Request
|
||||
makeWsRequest(std::string body)
|
||||
{
|
||||
return Request{std::move(body), httpHeaders_};
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(NgRpcServerHandlerTest, DosguardRejectedHttpRequest)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest("some message");
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(false));
|
||||
auto response = rpcServerHandler_(request, connectionMetadata_, nullptr, yield);
|
||||
|
||||
auto const responseHttp = std::move(response).intoHttpResponse();
|
||||
EXPECT_EQ(responseHttp.result(), http::status::service_unavailable);
|
||||
|
||||
auto const responseJson = boost::json::parse(responseHttp.body()).as_object();
|
||||
EXPECT_EQ(responseJson.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(NgRpcServerHandlerTest, DosguardRejectedWsRequest)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const requestStr = "some message";
|
||||
auto const request = makeWsRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(false));
|
||||
auto response = rpcServerHandler_(request, connectionMetadata_, nullptr, yield);
|
||||
|
||||
auto const responseWs = boost::beast::buffers_to_string(response.asWsResponse());
|
||||
|
||||
auto const responseJson = boost::json::parse(responseWs).as_object();
|
||||
EXPECT_EQ(responseJson.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
|
||||
EXPECT_EQ(responseJson.at("request").as_string(), requestStr);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(NgRpcServerHandlerTest, DosguardRejectedWsJsonRequest)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const requestStr = R"json({"request": "some message", "id": "some id"})json";
|
||||
auto const request = makeWsRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(false));
|
||||
auto response = rpcServerHandler_(request, connectionMetadata_, nullptr, yield);
|
||||
|
||||
auto const responseWs = boost::beast::buffers_to_string(response.asWsResponse());
|
||||
|
||||
auto const responseJson = boost::json::parse(responseWs).as_object();
|
||||
EXPECT_EQ(responseJson.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
|
||||
EXPECT_EQ(responseJson.at("request").as_string(), requestStr);
|
||||
EXPECT_EQ(responseJson.at("id").as_string(), "some id");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(NgRpcServerHandlerTest, PostToRpcEngineFailed)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest("some message");
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce(Return(false));
|
||||
EXPECT_CALL(*rpcEngine_, notifyTooBusy());
|
||||
auto response = rpcServerHandler_(request, connectionMetadata_, nullptr, yield);
|
||||
@@ -107,6 +170,8 @@ TEST_F(NgRpcServerHandlerTest, CoroutineSleepsUntilRpcEngineFinishes)
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest("some message");
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
boost::asio::spawn(
|
||||
ctx_,
|
||||
@@ -131,6 +196,8 @@ TEST_F(NgRpcServerHandlerTest, JsonParseFailed)
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest("not a json");
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(*rpcEngine_, notifyBadSyntax);
|
||||
fn(yield);
|
||||
@@ -141,10 +208,61 @@ TEST_F(NgRpcServerHandlerTest, JsonParseFailed)
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(NgRpcServerHandlerTest, DosguardRejectedParsedRequest)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
std::string const requestStr = "{}";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(false));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
fn(yield);
|
||||
return true;
|
||||
});
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
|
||||
auto response = rpcServerHandler_(request, connectionMetadata_, nullptr, yield);
|
||||
auto const responseHttp = std::move(response).intoHttpResponse();
|
||||
EXPECT_EQ(responseHttp.result(), http::status::service_unavailable);
|
||||
|
||||
auto const responseJson = boost::json::parse(responseHttp.body()).as_object();
|
||||
EXPECT_EQ(responseJson.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(NgRpcServerHandlerTest, DosguardAddsLoadWarning)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
std::string const requestStr = "{}";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(false));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
fn(yield);
|
||||
return true;
|
||||
});
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(false));
|
||||
|
||||
auto response = rpcServerHandler_(request, connectionMetadata_, nullptr, yield);
|
||||
auto const responseHttp = std::move(response).intoHttpResponse();
|
||||
EXPECT_EQ(responseHttp.result(), http::status::service_unavailable);
|
||||
|
||||
auto const responseJson = boost::json::parse(responseHttp.body()).as_object();
|
||||
EXPECT_EQ(responseJson.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
|
||||
|
||||
EXPECT_EQ(responseJson.at("warning").as_string(), "load");
|
||||
EXPECT_EQ(responseJson.at("warnings").as_array().at(0).as_object().at("id").as_int64(), rpc::WarnRpcRateLimit);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(NgRpcServerHandlerTest, GotNotJsonObject)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest("[]");
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(*rpcEngine_, notifyBadSyntax);
|
||||
fn(yield);
|
||||
@@ -158,8 +276,12 @@ TEST_F(NgRpcServerHandlerTest, GotNotJsonObject)
|
||||
TEST_F(NgRpcServerHandlerTest, HandleRequest_NoRangeFromBackend)
|
||||
{
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest("{}");
|
||||
std::string const requestStr = "{}";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillOnce(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, notifyNotReady);
|
||||
@@ -180,8 +302,12 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_ContextCreationFailed)
|
||||
{
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest("{}");
|
||||
std::string const requestStr = "{}";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, notifyBadSyntax);
|
||||
@@ -200,8 +326,12 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_BuildResponseFailed)
|
||||
{
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest(R"json({"method":"some_method"})json");
|
||||
std::string const requestStr = R"json({"method":"some_method"})json";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse)
|
||||
@@ -227,8 +357,12 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_BuildResponseThrewAnException)
|
||||
{
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest(R"json({"method":"some_method"})json");
|
||||
std::string const requestStr = R"json({"method":"some_method"})json";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse).WillOnce([](auto&&) -> rpc::Result {
|
||||
@@ -249,8 +383,12 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_Successful_HttpRequest)
|
||||
{
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest(R"json({"method":"some_method"})json");
|
||||
std::string const requestStr = R"json({"method":"some_method"})json";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse)
|
||||
@@ -278,8 +416,12 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_OutdatedWarning)
|
||||
{
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest(R"json({"method":"some_method"})json");
|
||||
std::string const requestStr = R"json({"method":"some_method"})json";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse)
|
||||
@@ -313,8 +455,12 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_Successful_HttpRequest_Forwarded)
|
||||
{
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest(R"json({"method":"some_method"})json");
|
||||
std::string const requestStr = R"json({"method":"some_method"})json";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse)
|
||||
@@ -345,8 +491,12 @@ TEST_F(NgRpcServerHandlerTest, HandleRequest_Successful_HttpRequest_HasError)
|
||||
{
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
auto const request = makeHttpRequest(R"json({"method":"some_method"})json");
|
||||
std::string const requestStr = R"json({"method":"some_method"})json";
|
||||
auto const request = makeHttpRequest(requestStr);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse)
|
||||
@@ -393,8 +543,12 @@ TEST_F(NgRpcServerHandlerWsTest, HandleRequest_Successful_WsRequest)
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
Request::HttpHeaders const headers;
|
||||
auto const request = Request(R"json({"method":"some_method", "id": 1234, "api_version": 1})json", headers);
|
||||
std::string const requestStr = R"json({"method":"some_method", "id": 1234, "api_version": 1})json";
|
||||
auto const request = Request(requestStr, headers);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse)
|
||||
@@ -424,8 +578,12 @@ TEST_F(NgRpcServerHandlerWsTest, HandleRequest_Successful_WsRequest_HasError)
|
||||
backend_->setRange(0, 1);
|
||||
runSpawn([&](boost::asio::yield_context yield) {
|
||||
Request::HttpHeaders const headers;
|
||||
auto const request = Request(R"json({"method":"some_method", "id": 1234, "api_version": 1})json", headers);
|
||||
std::string const requestStr = R"json({"method":"some_method", "id": 1234, "api_version": 1})json";
|
||||
auto const request = Request(requestStr, headers);
|
||||
|
||||
EXPECT_CALL(dosguard_, isOk(ip_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, request(ip_, boost::json::parse(requestStr).as_object())).WillOnce(Return(true));
|
||||
EXPECT_CALL(dosguard_, add(ip_, testing::_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*rpcEngine_, post).WillOnce([&](auto&& fn, auto&&) {
|
||||
EXPECT_CALL(connectionMetadata_, wasUpgraded).WillRepeatedly(Return(not request.isHttp()));
|
||||
EXPECT_CALL(*rpcEngine_, buildResponse)
|
||||
|
||||
Reference in New Issue
Block a user