mirror of
https://github.com/XRPLF/clio.git
synced 2025-12-06 17:27:58 +00:00
feat: Proxy support (#2490)
Add client IP resolving support in case when there is a proxy in front of Clio.
This commit is contained in:
214
tests/unit/web/ProxyIpResolverTests.cpp
Normal file
214
tests/unit/web/ProxyIpResolverTests.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2025, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "util/NameGenerator.hpp"
|
||||
#include "util/config/Array.hpp"
|
||||
#include "util/config/ConfigDefinition.hpp"
|
||||
#include "util/config/ConfigFileJson.hpp"
|
||||
#include "util/config/ConfigValue.hpp"
|
||||
#include "util/config/Types.hpp"
|
||||
#include "web/ProxyIpResolver.hpp"
|
||||
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace http = boost::beast::http;
|
||||
|
||||
using namespace web;
|
||||
|
||||
struct ProxyIpResolverTestParams {
|
||||
std::string testName;
|
||||
std::unordered_set<std::string> proxyIps;
|
||||
std::unordered_set<std::string> proxyTokens;
|
||||
std::vector<std::pair<std::string, std::string>> headers;
|
||||
std::string connectionIp;
|
||||
std::string expectedIp;
|
||||
};
|
||||
|
||||
class ProxyIpResolverTest : public ::testing::TestWithParam<ProxyIpResolverTestParams> {};
|
||||
|
||||
TEST_F(ProxyIpResolverTest, FromConfig)
|
||||
{
|
||||
using namespace util::config;
|
||||
ClioConfigDefinition config{{
|
||||
{"server.proxy.ips.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.proxy.tokens.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
}};
|
||||
auto const proxyIp = "1.2.3.4";
|
||||
auto const clientIp = "5.6.7.8";
|
||||
auto const proxyToken = "some_proxy_token";
|
||||
|
||||
auto const configStr = fmt::format(
|
||||
R"({{
|
||||
"server": {{
|
||||
"proxy": {{
|
||||
"ips": ["{}"],
|
||||
"tokens": ["{}"]
|
||||
}}
|
||||
}}
|
||||
}})",
|
||||
proxyIp,
|
||||
proxyToken
|
||||
);
|
||||
|
||||
auto const err = config.parse(ConfigFileJson{boost::json::parse(configStr).as_object()});
|
||||
ASSERT_FALSE(err.has_value());
|
||||
|
||||
auto const proxyIpResolver = ProxyIpResolver::fromConfig(config);
|
||||
ProxyIpResolver::HttpHeaders headers;
|
||||
|
||||
EXPECT_EQ(proxyIpResolver.resolveClientIp(clientIp, headers), clientIp);
|
||||
EXPECT_EQ(proxyIpResolver.resolveClientIp(proxyIp, headers), proxyIp);
|
||||
|
||||
headers.set(boost::beast::http::field::forwarded, fmt::format("for={}", clientIp));
|
||||
EXPECT_EQ(proxyIpResolver.resolveClientIp(clientIp, headers), clientIp);
|
||||
EXPECT_EQ(proxyIpResolver.resolveClientIp(proxyIp, headers), clientIp);
|
||||
|
||||
headers.set(ProxyIpResolver::kPROXY_TOKEN_HEADER, proxyToken);
|
||||
EXPECT_EQ(proxyIpResolver.resolveClientIp(clientIp, headers), clientIp);
|
||||
EXPECT_EQ(proxyIpResolver.resolveClientIp(proxyIp, headers), clientIp);
|
||||
EXPECT_EQ(proxyIpResolver.resolveClientIp("127.0.0.1", headers), clientIp);
|
||||
}
|
||||
|
||||
TEST_P(ProxyIpResolverTest, ResolveClientIp)
|
||||
{
|
||||
auto const& params = GetParam();
|
||||
ProxyIpResolver resolver(params.proxyIps, params.proxyTokens);
|
||||
ProxyIpResolver::HttpHeaders headers;
|
||||
for (auto const& [key, value] : params.headers) {
|
||||
headers.set(key, value);
|
||||
}
|
||||
EXPECT_EQ(resolver.resolveClientIp(params.connectionIp, headers), params.expectedIp);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ProxyIpResolverTests,
|
||||
ProxyIpResolverTest,
|
||||
::testing::Values(
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "NoProxy",
|
||||
.proxyIps = {},
|
||||
.proxyTokens = {},
|
||||
.headers = {},
|
||||
.connectionIp = "1.2.3.4",
|
||||
.expectedIp = "1.2.3.4"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "TrustedProxyIpWithForwardedHeader",
|
||||
.proxyIps = {"5.6.7.8"},
|
||||
.proxyTokens = {},
|
||||
.headers = {{std::string(http::to_string(http::field::forwarded)), "for=1.2.3.4"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "1.2.3.4"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "TrustedProxyIpWithoutForwardedHeader",
|
||||
.proxyIps = {"5.6.7.8"},
|
||||
.proxyTokens = {},
|
||||
.headers = {},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "5.6.7.8"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "UntrustedProxyIpWithForwardedHeader",
|
||||
.proxyIps = {},
|
||||
.proxyTokens = {},
|
||||
.headers = {{std::string(http::to_string(http::field::forwarded)), "for=1.2.3.4"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "5.6.7.8"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "TrustedProxyTokenWithForwardedHeader",
|
||||
.proxyIps = {},
|
||||
.proxyTokens = {"test_token"},
|
||||
.headers =
|
||||
{{std::string(ProxyIpResolver::kPROXY_TOKEN_HEADER), "test_token"},
|
||||
{std::string(http::to_string(http::field::forwarded)), "for=1.2.3.4"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "1.2.3.4"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "TrustedProxyTokenWithoutForwardedHeader",
|
||||
.proxyIps = {},
|
||||
.proxyTokens = {"test_token"},
|
||||
.headers = {{std::string(ProxyIpResolver::kPROXY_TOKEN_HEADER), "test_token"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "5.6.7.8"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "UntrustedProxyTokenWithForwardedHeader",
|
||||
.proxyIps = {},
|
||||
.proxyTokens = {},
|
||||
.headers =
|
||||
{{std::string(ProxyIpResolver::kPROXY_TOKEN_HEADER), "test_token"},
|
||||
{std::string(http::to_string(http::field::forwarded)), "for=1.2.3.4"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "5.6.7.8"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "ForwardedHeaderWithAdditionalFields",
|
||||
.proxyIps = {"5.6.7.8"},
|
||||
.proxyTokens = {},
|
||||
.headers =
|
||||
{{std::string(http::to_string(http::field::forwarded)),
|
||||
"by=203.0.113.43; for=1.2.3.4; host=example.com; proto=https"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "1.2.3.4"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "ForwardedHeaderWithDifferentCase",
|
||||
.proxyIps = {"5.6.7.8"},
|
||||
.proxyTokens = {},
|
||||
.headers = {{std::string(http::to_string(http::field::forwarded)), "For=1.2.3.4"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "1.2.3.4"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "ForwardedHeaderWithoutFor",
|
||||
.proxyIps = {"5.6.7.8"},
|
||||
.proxyTokens = {},
|
||||
.headers = {{std::string(http::to_string(http::field::forwarded)), "by=1.2.3.4"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "5.6.7.8"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "ForwardedHeaderWithIpInQuotes",
|
||||
.proxyIps = {"5.6.7.8"},
|
||||
.proxyTokens = {},
|
||||
.headers = {{std::string(http::to_string(http::field::forwarded)), "for=\"1.2.3.4\""}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "1.2.3.4"
|
||||
},
|
||||
ProxyIpResolverTestParams{
|
||||
.testName = "ForwardedHeaderIsIncorrect",
|
||||
.proxyIps = {"5.6.7.8"},
|
||||
.proxyTokens = {},
|
||||
.headers = {{std::string(http::to_string(http::field::forwarded)), "for=\";some_other_text"}},
|
||||
.connectionIp = "5.6.7.8",
|
||||
.expectedIp = "5.6.7.8"
|
||||
}
|
||||
),
|
||||
tests::util::kNAME_GENERATOR
|
||||
);
|
||||
@@ -132,8 +132,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPDefaultPath)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -153,7 +153,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPRejectedByDosguard)
|
||||
"params": [{}]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(false));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(false));
|
||||
|
||||
(*handler)(kREQUEST, session);
|
||||
EXPECT_EQ(session->slowDownCallsCounter, 1);
|
||||
@@ -166,8 +166,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPRejectedByDosguardAfterParsing)
|
||||
"params": [{}]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(false));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), testing::_)).WillOnce(testing::Return(false));
|
||||
|
||||
(*handler)(kREQUEST, session);
|
||||
EXPECT_EQ(session->slowDownCallsCounter, 1);
|
||||
@@ -198,8 +198,8 @@ TEST_F(WebRPCServerHandlerTest, WsNormalPath)
|
||||
}
|
||||
]
|
||||
})JSON";
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -221,7 +221,7 @@ TEST_F(WebRPCServerHandlerTest, WsRejectedByDosguard)
|
||||
"api_version": 2
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(false));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(false));
|
||||
|
||||
(*handler)(kREQUEST, session);
|
||||
EXPECT_EQ(session->slowDownCallsCounter, 1);
|
||||
@@ -236,8 +236,8 @@ TEST_F(WebRPCServerHandlerTest, WsRejectedByDosguardAfterParsing)
|
||||
"api_version": 2
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(false));
|
||||
|
||||
(*handler)(kREQUEST, session);
|
||||
@@ -274,8 +274,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedPath)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -323,8 +323,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPForwardedErrorPath)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -370,8 +370,8 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedPath)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -420,8 +420,8 @@ TEST_F(WebRPCServerHandlerTest, WsForwardedErrorPath)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -472,8 +472,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPErrorPath)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -521,8 +521,8 @@ TEST_F(WebRPCServerHandlerTest, WsErrorPath)
|
||||
"api_version": 2
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -557,8 +557,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPNotReady)
|
||||
}
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyNotReady).Times(1);
|
||||
@@ -589,8 +589,8 @@ TEST_F(WebRPCServerHandlerTest, WsNotReady)
|
||||
}
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyNotReady).Times(1);
|
||||
@@ -619,8 +619,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPBadSyntaxWhenRequestSubscribe)
|
||||
}
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), testing::_)).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -636,8 +636,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPMissingCommand)
|
||||
|
||||
static constexpr auto kRESPONSE = "Null method";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), testing::_)).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -654,8 +654,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPCommandNotString)
|
||||
|
||||
static constexpr auto kRESPONSE = "method is not string";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), testing::_)).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -672,8 +672,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPCommandIsEmpty)
|
||||
|
||||
static constexpr auto kRESPONSE = "method is empty";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), testing::_)).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -705,8 +705,8 @@ TEST_F(WebRPCServerHandlerTest, WsMissingCommand)
|
||||
}
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
@@ -726,8 +726,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparsableNotArray)
|
||||
"params": "wrong"
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), testing::_)).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -747,8 +747,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparsableArrayWithDigit)
|
||||
"params": [1]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, testing::_)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), testing::_)).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -780,8 +780,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPInternalError)
|
||||
"params": [{}]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyInternalError).Times(1);
|
||||
@@ -815,8 +815,8 @@ TEST_F(WebRPCServerHandlerTest, WsInternalError)
|
||||
"id": "123"
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST_JSON).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyInternalError).Times(1);
|
||||
@@ -852,8 +852,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPOutDated)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -895,8 +895,8 @@ TEST_F(WebRPCServerHandlerTest, WsOutdated)
|
||||
]
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, buildResponse(testing::_))
|
||||
@@ -931,8 +931,8 @@ TEST_F(WebRPCServerHandlerTest, WsTooBusy)
|
||||
"type": "response"
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*localRpcEngine, notifyTooBusy).Times(1);
|
||||
@@ -962,8 +962,8 @@ TEST_F(WebRPCServerHandlerTest, HTTPTooBusy)
|
||||
"type": "response"
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(kREQUEST).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(kREQUEST).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*localRpcEngine, notifyTooBusy).Times(1);
|
||||
@@ -978,7 +978,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPRequestNotJson)
|
||||
static constexpr auto kREQUEST = "not json";
|
||||
static constexpr auto kRESPONSE_PREFIX = "Unable to parse JSON from the request";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -1000,7 +1000,7 @@ TEST_F(WebRPCServerHandlerTest, WsRequestNotJson)
|
||||
"type": "response"
|
||||
})JSON";
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -1058,8 +1058,8 @@ TEST_P(WebRPCServerHandlerInvalidAPIVersionParamTest, HTTPInvalidAPIVersion)
|
||||
|
||||
backend_->setRange(kMIN_SEQ, kMAX_SEQ);
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(request).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(request).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
@@ -1082,8 +1082,8 @@ TEST_P(WebRPCServerHandlerInvalidAPIVersionParamTest, WSInvalidAPIVersion)
|
||||
|
||||
backend_->setRange(kMIN_SEQ, kMAX_SEQ);
|
||||
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp)).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp, boost::json::parse(request).as_object()))
|
||||
EXPECT_CALL(dosguard, isOk(session->clientIp())).WillOnce(testing::Return(true));
|
||||
EXPECT_CALL(dosguard, request(session->clientIp(), boost::json::parse(request).as_object()))
|
||||
.WillOnce(testing::Return(true));
|
||||
|
||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||
|
||||
@@ -124,6 +124,8 @@ getParseServerConfig(boost::json::value val)
|
||||
{"server.port", ConfigValue{ConfigType::Integer}},
|
||||
{"server.admin_password", ConfigValue{ConfigType::String}.optional()},
|
||||
{"server.local_admin", ConfigValue{ConfigType::Boolean}.optional()},
|
||||
{"server.proxy.ips.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.proxy.tokens.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.ws_max_sending_queue_size", ConfigValue{ConfigType::Integer}.defaultValue(1500)},
|
||||
{"log.tag_style", ConfigValue{ConfigType::String}.defaultValue("uint")},
|
||||
{"dos_guard.max_fetches", ConfigValue{ConfigType::Integer}},
|
||||
@@ -519,6 +521,8 @@ getParseAdminServerConfig(boost::json::value val)
|
||||
{"server.admin_password", ConfigValue{ConfigType::String}.optional()},
|
||||
{"server.local_admin", ConfigValue{ConfigType::Boolean}.optional()},
|
||||
{"server.processing_policy", ConfigValue{ConfigType::String}.defaultValue("parallel")},
|
||||
{"server.proxy.ips.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.proxy.tokens.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.parallel_requests_limit", ConfigValue{ConfigType::Integer}.optional()},
|
||||
{"server.ws_max_sending_queue_size", ConfigValue{ConfigType::Integer}.defaultValue(1500)},
|
||||
{"ssl_cert_file", ConfigValue{ConfigType::String}.optional()},
|
||||
|
||||
@@ -26,11 +26,13 @@
|
||||
#include "util/Taggable.hpp"
|
||||
#include "util/TestHttpClient.hpp"
|
||||
#include "util/TestWebSocketClient.hpp"
|
||||
#include "util/config/Array.hpp"
|
||||
#include "util/config/ConfigConstraints.hpp"
|
||||
#include "util/config/ConfigDefinition.hpp"
|
||||
#include "util/config/ConfigFileJson.hpp"
|
||||
#include "util/config/ConfigValue.hpp"
|
||||
#include "util/config/Types.hpp"
|
||||
#include "web/ProxyIpResolver.hpp"
|
||||
#include "web/SubscriptionContextInterface.hpp"
|
||||
#include "web/ng/Connection.hpp"
|
||||
#include "web/ng/ProcessingPolicy.hpp"
|
||||
@@ -58,6 +60,7 @@
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace web::ng;
|
||||
using namespace util::config;
|
||||
@@ -85,6 +88,8 @@ TEST_P(MakeServerTest, Make)
|
||||
{"server.ip", ConfigValue{ConfigType::String}.optional()},
|
||||
{"server.port", ConfigValue{ConfigType::Integer}.optional()},
|
||||
{"server.processing_policy", ConfigValue{ConfigType::String}.defaultValue("parallel")},
|
||||
{"server.proxy.ips.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.proxy.tokens.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.parallel_requests_limit", ConfigValue{ConfigType::Integer}.optional()},
|
||||
{"server.ws_max_sending_queue_size", ConfigValue{ConfigType::Integer}.defaultValue(1500)},
|
||||
{"log.tag_style", ConfigValue{ConfigType::String}.defaultValue("uint")},
|
||||
@@ -95,8 +100,13 @@ TEST_P(MakeServerTest, Make)
|
||||
auto const errors = config.parse(json);
|
||||
ASSERT_TRUE(!errors.has_value());
|
||||
|
||||
auto const expectedServer =
|
||||
makeServer(config, [](auto&&) -> std::expected<void, Response> { return {}; }, [](auto&&) {}, ioContext_);
|
||||
auto const expectedServer = makeServer(
|
||||
config,
|
||||
[](auto&&) -> std::expected<void, Response> { return {}; },
|
||||
[](auto&&, auto&&) {},
|
||||
[](auto&&) {},
|
||||
ioContext_
|
||||
);
|
||||
EXPECT_EQ(expectedServer.has_value(), GetParam().expectSuccess);
|
||||
}
|
||||
|
||||
@@ -173,6 +183,8 @@ protected:
|
||||
{"server.admin_password", ConfigValue{ConfigType::String}.optional()},
|
||||
{"server.local_admin", ConfigValue{ConfigType::Boolean}.optional()},
|
||||
{"server.parallel_requests_limit", ConfigValue{ConfigType::Integer}.optional()},
|
||||
{"server.proxy.ips.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.proxy.tokens.[]", Array{ConfigValue{ConfigType::String}}},
|
||||
{"server.ws_max_sending_queue_size", ConfigValue{ConfigType::Integer}.defaultValue(1500)},
|
||||
{"log.tag_style", ConfigValue{ConfigType::String}.defaultValue("uint")},
|
||||
{"ssl_key_file", ConfigValue{ConfigType::String}.optional()},
|
||||
@@ -180,7 +192,8 @@ protected:
|
||||
};
|
||||
|
||||
Server::OnConnectCheck emptyOnConnectCheck_ = [](auto&&) -> std::expected<void, Response> { return {}; };
|
||||
std::expected<Server, std::string> server_ = makeServer(config_, emptyOnConnectCheck_, [](auto&&) {}, ctx_);
|
||||
std::expected<Server, std::string> server_ =
|
||||
makeServer(config_, emptyOnConnectCheck_, [](auto&&, auto&&) {}, [](auto&&) {}, ctx_);
|
||||
|
||||
std::string requestMessage_ = "some request";
|
||||
std::string const headerName_ = "Some-header";
|
||||
@@ -210,9 +223,13 @@ TEST_F(ServerTest, BadEndpoint)
|
||||
ProcessingPolicy::Sequential,
|
||||
std::nullopt,
|
||||
tagDecoratorFactory,
|
||||
web::ProxyIpResolver{{}, {}},
|
||||
std::nullopt,
|
||||
emptyOnConnectCheck_,
|
||||
[](auto&&) {}
|
||||
Server::Hooks{
|
||||
.onConnectCheck = emptyOnConnectCheck_,
|
||||
.onIpChangeHook = [](auto&&, auto&&) {},
|
||||
.onDisconnectHook = [](auto&&) {}
|
||||
}
|
||||
};
|
||||
|
||||
auto maybeError = server.run();
|
||||
@@ -274,9 +291,13 @@ TEST_F(ServerHttpTest, OnConnectCheck)
|
||||
ProcessingPolicy::Sequential,
|
||||
std::nullopt,
|
||||
tagDecoratorFactory,
|
||||
web::ProxyIpResolver{{}, {}},
|
||||
std::nullopt,
|
||||
onConnectCheck.AsStdFunction(),
|
||||
[](auto&&) {}
|
||||
Server::Hooks{
|
||||
.onConnectCheck = onConnectCheck.AsStdFunction(),
|
||||
.onIpChangeHook = [](auto&&, auto&&) {},
|
||||
.onDisconnectHook = [](auto&&) {}
|
||||
}
|
||||
};
|
||||
|
||||
HttpAsyncClient client{ctx_};
|
||||
@@ -334,9 +355,13 @@ TEST_F(ServerHttpTest, OnConnectCheckFailed)
|
||||
ProcessingPolicy::Sequential,
|
||||
std::nullopt,
|
||||
tagDecoratorFactory,
|
||||
web::ProxyIpResolver{{}, {}},
|
||||
std::nullopt,
|
||||
onConnectCheck.AsStdFunction(),
|
||||
[](auto&&) {}
|
||||
Server::Hooks{
|
||||
.onConnectCheck = onConnectCheck.AsStdFunction(),
|
||||
.onIpChangeHook = [](auto&&, auto&&) {},
|
||||
.onDisconnectHook = [](auto&&) {}
|
||||
}
|
||||
};
|
||||
|
||||
HttpAsyncClient client{ctx_};
|
||||
@@ -393,9 +418,13 @@ TEST_F(ServerHttpTest, OnDisconnectHook)
|
||||
ProcessingPolicy::Sequential,
|
||||
std::nullopt,
|
||||
tagDecoratorFactory,
|
||||
web::ProxyIpResolver{{}, {}},
|
||||
std::nullopt,
|
||||
emptyOnConnectCheck_,
|
||||
onDisconnectHookMock.AsStdFunction()
|
||||
Server::Hooks{
|
||||
.onConnectCheck = emptyOnConnectCheck_,
|
||||
.onIpChangeHook = [](auto&&, auto&&) {},
|
||||
.onDisconnectHook = onDisconnectHookMock.AsStdFunction()
|
||||
}
|
||||
};
|
||||
|
||||
HttpAsyncClient client{ctx_};
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "util/config/ConfigDefinition.hpp"
|
||||
#include "util/config/ConfigValue.hpp"
|
||||
#include "util/config/Types.hpp"
|
||||
#include "web/ProxyIpResolver.hpp"
|
||||
#include "web/SubscriptionContextInterface.hpp"
|
||||
#include "web/ng/Connection.hpp"
|
||||
#include "web/ng/Error.hpp"
|
||||
@@ -40,11 +41,13 @@
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/beast/core/flat_buffer.hpp>
|
||||
#include <boost/beast/http/error.hpp>
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/beast/http/message.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
#include <boost/beast/http/verb.hpp>
|
||||
#include <boost/beast/websocket/error.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -60,7 +63,6 @@ using namespace web::ng::impl;
|
||||
using namespace web::ng;
|
||||
using namespace util;
|
||||
using testing::Return;
|
||||
namespace beast = boost::beast;
|
||||
namespace http = boost::beast::http;
|
||||
namespace websocket = boost::beast::websocket;
|
||||
|
||||
@@ -69,7 +71,15 @@ struct ConnectionHandlerTest : prometheus::WithPrometheus, SyncAsioContextTest {
|
||||
: tagFactory{util::config::ClioConfigDefinition{
|
||||
{"log.tag_style", config::ConfigValue{config::ConfigType::String}.defaultValue("uint")}
|
||||
}}
|
||||
, connectionHandler{policy, maxParallelConnections, tagFactory, std::nullopt, onDisconnectMock.AsStdFunction()}
|
||||
, connectionHandler{
|
||||
policy,
|
||||
maxParallelConnections,
|
||||
tagFactory,
|
||||
std::nullopt,
|
||||
proxyIpResolver,
|
||||
onDisconnectMock.AsStdFunction(),
|
||||
onIpChangeMock.AsStdFunction()
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -98,6 +108,12 @@ struct ConnectionHandlerTest : prometheus::WithPrometheus, SyncAsioContextTest {
|
||||
return Request{std::forward<Args>(args)...};
|
||||
}
|
||||
|
||||
std::string const clientIp = "1.2.3.4";
|
||||
std::string const proxyIp = "5.6.7.8";
|
||||
std::string const proxyToken = "some_proxy_token";
|
||||
web::ProxyIpResolver proxyIpResolver{{proxyIp}, {proxyToken}};
|
||||
|
||||
testing::StrictMock<testing::MockFunction<void(std::string const&, std::string const&)>> onIpChangeMock;
|
||||
testing::StrictMock<testing::MockFunction<void(Connection const&)>> onDisconnectMock;
|
||||
util::TagDecoratorFactory tagFactory;
|
||||
ConnectionHandler connectionHandler;
|
||||
@@ -106,9 +122,9 @@ struct ConnectionHandlerTest : prometheus::WithPrometheus, SyncAsioContextTest {
|
||||
{"log.tag_style", config::ConfigValue{config::ConfigType::String}.defaultValue("uint")}
|
||||
}};
|
||||
StrictMockHttpConnectionPtr mockHttpConnection =
|
||||
std::make_unique<StrictMockHttpConnection>("1.2.3.4", beast::flat_buffer{}, tagDecoratorFactory);
|
||||
std::make_unique<StrictMockHttpConnection>(clientIp, boost::beast::flat_buffer{}, tagDecoratorFactory);
|
||||
StrictMockWsConnectionPtr mockWsConnection =
|
||||
std::make_unique<StrictMockWsConnection>("1.2.3.4", beast::flat_buffer{}, tagDecoratorFactory);
|
||||
std::make_unique<StrictMockWsConnection>(clientIp, boost::beast::flat_buffer{}, tagDecoratorFactory);
|
||||
|
||||
Request::HttpHeaders headers;
|
||||
};
|
||||
@@ -452,6 +468,50 @@ TEST_F(ConnectionHandlerSequentialProcessingTest, Receive_Handle_SendError)
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ConnectionHandlerSequentialProcessingTest, OnIpChangeHookCalledWhenSentFromProxy)
|
||||
{
|
||||
std::string const target = "/some/target";
|
||||
testing::StrictMock<testing::MockFunction<
|
||||
Response(Request const&, ConnectionMetadata const&, web::SubscriptionContextPtr, boost::asio::yield_context)>>
|
||||
getHandlerMock;
|
||||
|
||||
std::string const requestMessage = "some message";
|
||||
std::string const responseMessage = "some response";
|
||||
|
||||
connectionHandler.onGet(target, getHandlerMock.AsStdFunction());
|
||||
|
||||
StrictMockHttpConnectionPtr mockHttpConnectionFromProxy =
|
||||
std::make_unique<StrictMockHttpConnection>(proxyIp, boost::beast::flat_buffer{}, tagDecoratorFactory);
|
||||
|
||||
auto request = http::request<http::string_body>{http::verb::get, target, 11, requestMessage};
|
||||
request.set(http::field::forwarded, fmt::format("for={}", clientIp));
|
||||
|
||||
EXPECT_CALL(*mockHttpConnectionFromProxy, wasUpgraded).WillOnce(Return(false));
|
||||
EXPECT_CALL(*mockHttpConnectionFromProxy, receive).WillOnce(Return(makeRequest(request)));
|
||||
|
||||
EXPECT_CALL(onIpChangeMock, Call(proxyIp, clientIp));
|
||||
|
||||
EXPECT_CALL(getHandlerMock, Call).WillOnce([&](Request const& request, auto&&, auto&&, auto&&) {
|
||||
EXPECT_EQ(request.message(), requestMessage);
|
||||
return Response(http::status::ok, responseMessage, request);
|
||||
});
|
||||
|
||||
EXPECT_CALL(*mockHttpConnectionFromProxy, send).WillOnce([&responseMessage](Response response, auto&&) {
|
||||
EXPECT_EQ(response.message(), responseMessage);
|
||||
return makeError(http::error::end_of_stream).error();
|
||||
});
|
||||
|
||||
EXPECT_CALL(onDisconnectMock, Call)
|
||||
.WillOnce([this, connectionPtr = mockHttpConnectionFromProxy.get()](Connection const& c) {
|
||||
EXPECT_EQ(&c, connectionPtr);
|
||||
EXPECT_EQ(c.ip(), clientIp);
|
||||
});
|
||||
|
||||
runSpawn([this, c = std::move(mockHttpConnectionFromProxy)](boost::asio::yield_context yield) mutable {
|
||||
connectionHandler.processConnection(std::move(c), yield);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ConnectionHandlerSequentialProcessingTest, Stop)
|
||||
{
|
||||
testing::StrictMock<testing::MockFunction<
|
||||
@@ -613,6 +673,48 @@ TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send)
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ConnectionHandlerParallelProcessingTest, OnIpChangeHookCalledWhenSentFromProxy)
|
||||
{
|
||||
testing::StrictMock<testing::MockFunction<
|
||||
Response(Request const&, ConnectionMetadata const&, web::SubscriptionContextPtr, boost::asio::yield_context)>>
|
||||
wsHandlerMock;
|
||||
connectionHandler.onWs(wsHandlerMock.AsStdFunction());
|
||||
|
||||
StrictMockWsConnectionPtr mockWsConnectionFromProxy =
|
||||
std::make_unique<StrictMockWsConnection>(proxyIp, boost::beast::flat_buffer{}, tagDecoratorFactory);
|
||||
headers.set(http::field::forwarded, fmt::format("for={}", clientIp));
|
||||
|
||||
std::string const requestMessage = "some message";
|
||||
std::string const responseMessage = "some response";
|
||||
|
||||
EXPECT_CALL(*mockWsConnectionFromProxy, wasUpgraded).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mockWsConnectionFromProxy, receive)
|
||||
.WillOnce(Return(makeRequest(requestMessage, headers)))
|
||||
.WillOnce(Return(makeError(websocket::error::closed)));
|
||||
|
||||
EXPECT_CALL(onIpChangeMock, Call(proxyIp, clientIp));
|
||||
|
||||
EXPECT_CALL(wsHandlerMock, Call).WillOnce([&](Request const& request, auto&&, auto&&, auto&&) {
|
||||
EXPECT_EQ(request.message(), requestMessage);
|
||||
return Response(http::status::ok, responseMessage, request);
|
||||
});
|
||||
|
||||
EXPECT_CALL(*mockWsConnectionFromProxy, send).WillOnce([&responseMessage](Response response, auto&&) {
|
||||
EXPECT_EQ(response.message(), responseMessage);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
EXPECT_CALL(onDisconnectMock, Call)
|
||||
.WillOnce([this, connectionPtr = mockWsConnectionFromProxy.get()](Connection const& c) {
|
||||
EXPECT_EQ(&c, connectionPtr);
|
||||
EXPECT_EQ(c.ip(), clientIp);
|
||||
});
|
||||
|
||||
runSpawn([this, c = std::move(mockWsConnectionFromProxy)](boost::asio::yield_context yield) mutable {
|
||||
connectionHandler.processConnection(std::move(c), yield);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(ConnectionHandlerParallelProcessingTest, Receive_Handle_Send_Loop)
|
||||
{
|
||||
testing::StrictMock<testing::MockFunction<
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <boost/beast/http/string_body.hpp>
|
||||
#include <boost/beast/http/verb.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/beast/core/flat_buffer.hpp>
|
||||
#include <boost/beast/http/field.hpp>
|
||||
#include <boost/beast/http/status.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@@ -65,7 +67,10 @@ struct WebWsConnectionTests : SyncAsioContextTest {
|
||||
auto ip = expectedSocket->remote_endpoint().address().to_string();
|
||||
|
||||
PlainHttpConnection httpConnection{
|
||||
std::move(expectedSocket).value(), std::move(ip), boost::beast::flat_buffer{}, tagDecoratorFactory_
|
||||
std::move(expectedSocket).value(),
|
||||
std::move(ip),
|
||||
boost::beast::flat_buffer{},
|
||||
tagDecoratorFactory_,
|
||||
};
|
||||
|
||||
auto expectedTrue = httpConnection.isUpgradeRequested(yield);
|
||||
|
||||
Reference in New Issue
Block a user