feat: Dosguard API weights (#2082)

Experimental support for Dosguard API weights.
This commit is contained in:
Sergey Kuznetsov
2025-05-13 15:02:21 +01:00
committed by GitHub
parent d02da5d28a
commit b87b32db86
42 changed files with 1653 additions and 329 deletions

View File

@@ -165,75 +165,13 @@ struct RequestHandlerTest : SyncAsioContextTest, WebHandlersTest {
testing::StrictMock<RpcHandlerMock> rpcHandler;
StrictMockConnection connectionMock{ip, boost::beast::flat_buffer{}, tagFactory};
RequestHandler<RpcHandlerMock> requestHandler{adminVerifier, rpcHandler, dosGuardMock};
RequestHandler<RpcHandlerMock> requestHandler{adminVerifier, rpcHandler};
};
TEST_F(RequestHandlerTest, DosguardRateLimited_Http)
{
web::ng::Request const request{http::request<http::string_body>{http::verb::get, "/", 11}};
EXPECT_CALL(dosGuardMock, request(ip)).WillOnce(testing::Return(false));
runSpawn([&](boost::asio::yield_context yield) {
auto response = requestHandler(request, connectionMock, nullptr, yield);
auto const httpResponse = std::move(response).intoHttpResponse();
EXPECT_EQ(httpResponse.result(), boost::beast::http::status::service_unavailable);
auto const body = boost::json::parse(httpResponse.body()).as_object();
EXPECT_EQ(body.at("error").as_string(), "slowDown");
EXPECT_EQ(body.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
EXPECT_EQ(body.at("status").as_string(), "error");
EXPECT_FALSE(body.contains("id"));
EXPECT_FALSE(body.contains("request"));
});
}
TEST_F(RequestHandlerTest, DosguardRateLimited_Ws)
{
auto const requestMessage = R"json({"some": "request", "id": "some id"})json";
web::ng::Request::HttpHeaders const headers{};
web::ng::Request const request{requestMessage, headers};
EXPECT_CALL(dosGuardMock, request(ip)).WillOnce(testing::Return(false));
runSpawn([&](boost::asio::yield_context yield) {
auto const response = requestHandler(request, connectionMock, nullptr, yield);
auto const message = boost::json::parse(response.message()).as_object();
EXPECT_EQ(message.at("error").as_string(), "slowDown");
EXPECT_EQ(message.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
EXPECT_EQ(message.at("status").as_string(), "error");
EXPECT_EQ(message.at("id").as_string(), "some id");
EXPECT_EQ(message.at("request").as_string(), requestMessage);
});
}
TEST_F(RequestHandlerTest, DosguardRateLimited_Ws_ErrorParsing)
{
auto const requestMessage = R"json(some request "id": "some id")json";
web::ng::Request::HttpHeaders const headers{};
web::ng::Request const request{requestMessage, headers};
EXPECT_CALL(dosGuardMock, request(ip)).WillOnce(testing::Return(false));
runSpawn([&](boost::asio::yield_context yield) {
auto const response = requestHandler(request, connectionMock, nullptr, yield);
auto const message = boost::json::parse(response.message()).as_object();
EXPECT_EQ(message.at("error").as_string(), "slowDown");
EXPECT_EQ(message.at("error_code").as_int64(), rpc::RippledError::rpcSLOW_DOWN);
EXPECT_EQ(message.at("status").as_string(), "error");
EXPECT_FALSE(message.contains("id"));
EXPECT_EQ(message.at("request").as_string(), requestMessage);
});
}
TEST_F(RequestHandlerTest, RpcHandlerThrows)
{
web::ng::Request const request{http::request<http::string_body>{http::verb::get, "/", 11}};
EXPECT_CALL(dosGuardMock, request(ip)).WillOnce(testing::Return(true));
EXPECT_CALL(*adminVerifier, isAdmin).WillOnce(testing::Return(true));
EXPECT_CALL(rpcHandler, call).WillOnce(testing::Throw(std::runtime_error{"some error"}));
@@ -257,10 +195,8 @@ TEST_F(RequestHandlerTest, NoErrors)
web::ng::Response const response{http::status::ok, "some response", request};
auto const httpResponse = web::ng::Response{response}.intoHttpResponse();
EXPECT_CALL(dosGuardMock, request(ip)).WillOnce(testing::Return(true));
EXPECT_CALL(*adminVerifier, isAdmin).WillOnce(testing::Return(true));
EXPECT_CALL(rpcHandler, call).WillOnce(testing::Return(response));
EXPECT_CALL(dosGuardMock, add(ip, testing::_)).WillOnce(testing::Return(true));
runSpawn([&](boost::asio::yield_context yield) {
auto actualResponse = requestHandler(request, connectionMock, nullptr, yield);
@@ -272,55 +208,3 @@ TEST_F(RequestHandlerTest, NoErrors)
EXPECT_EQ(actualHttpResponse.version(), 11);
});
}
TEST_F(RequestHandlerTest, ResponseDosGuardWarning_ResponseHasWarnings)
{
web::ng::Request const request{http::request<http::string_body>{http::verb::get, "/", 11}};
web::ng::Response const response{
http::status::ok, R"json({"some":"response", "warnings":["some warning"]})json", request
};
auto const httpResponse = web::ng::Response{response}.intoHttpResponse();
EXPECT_CALL(dosGuardMock, request(ip)).WillOnce(testing::Return(true));
EXPECT_CALL(*adminVerifier, isAdmin).WillOnce(testing::Return(true));
EXPECT_CALL(rpcHandler, call).WillOnce(testing::Return(response));
EXPECT_CALL(dosGuardMock, add(ip, testing::_)).WillOnce(testing::Return(false));
runSpawn([&](boost::asio::yield_context yield) {
auto actualResponse = requestHandler(request, connectionMock, nullptr, yield);
auto const actualHttpResponse = std::move(actualResponse).intoHttpResponse();
EXPECT_EQ(actualHttpResponse.result(), httpResponse.result());
EXPECT_EQ(actualHttpResponse.version(), 11);
auto actualBody = boost::json::parse(actualHttpResponse.body()).as_object();
EXPECT_EQ(actualBody.at("some").as_string(), "response");
EXPECT_EQ(actualBody.at("warnings").as_array().size(), 2);
});
}
TEST_F(RequestHandlerTest, ResponseDosGuardWarning_ResponseDoesntHaveWarnings)
{
web::ng::Request const request{http::request<http::string_body>{http::verb::get, "/", 11}};
web::ng::Response const response{http::status::ok, R"json({"some":"response"})json", request};
auto const httpResponse = web::ng::Response{response}.intoHttpResponse();
EXPECT_CALL(dosGuardMock, request(ip)).WillOnce(testing::Return(true));
EXPECT_CALL(*adminVerifier, isAdmin).WillOnce(testing::Return(true));
EXPECT_CALL(rpcHandler, call).WillOnce(testing::Return(response));
EXPECT_CALL(dosGuardMock, add(ip, testing::_)).WillOnce(testing::Return(false));
runSpawn([&](boost::asio::yield_context yield) {
auto actualResponse = requestHandler(request, connectionMock, nullptr, yield);
auto const actualHttpResponse = std::move(actualResponse).intoHttpResponse();
EXPECT_EQ(actualHttpResponse.result(), httpResponse.result());
EXPECT_EQ(actualHttpResponse.version(), 11);
auto actualBody = boost::json::parse(actualHttpResponse.body()).as_object();
EXPECT_EQ(actualBody.at("some").as_string(), "response");
EXPECT_EQ(actualBody.at("warnings").as_array().size(), 1);
});
}