mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-04 11:55:51 +00:00
@@ -93,9 +93,9 @@ ClioApplication::run()
|
||||
boost::asio::io_context ioc{threads};
|
||||
|
||||
// Rate limiter, to prevent abuse
|
||||
auto sweepHandler = web::IntervalSweepHandler{config_, ioc};
|
||||
auto whitelistHandler = web::WhitelistHandler{config_};
|
||||
auto dosGuard = web::DOSGuard{config_, whitelistHandler, sweepHandler};
|
||||
auto dosGuard = web::DOSGuard{config_, whitelistHandler};
|
||||
auto sweepHandler = web::IntervalSweepHandler{config_, ioc, dosGuard};
|
||||
|
||||
// Interface to the database
|
||||
auto backend = data::make_Backend(config_);
|
||||
|
||||
@@ -10,6 +10,7 @@ target_sources(
|
||||
NetworkValidatedLedgers.cpp
|
||||
NFTHelpers.cpp
|
||||
Source.cpp
|
||||
impl/AmendmentBlockHandler.cpp
|
||||
impl/ForwardingCache.cpp
|
||||
impl/ForwardingSource.cpp
|
||||
impl/GrpcSource.cpp
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "data/LedgerCache.hpp"
|
||||
#include "etl/CacheLoader.hpp"
|
||||
#include "etl/ETLHelpers.hpp"
|
||||
#include "etl/ETLState.hpp"
|
||||
#include "etl/LoadBalancer.hpp"
|
||||
#include "etl/NetworkValidatedLedgersInterface.hpp"
|
||||
#include "etl/SystemState.hpp"
|
||||
#include "etl/impl/AmendmentBlock.hpp"
|
||||
#include "etl/impl/AmendmentBlockHandler.hpp"
|
||||
#include "etl/impl/ExtractionDataPipe.hpp"
|
||||
#include "etl/impl/Extractor.hpp"
|
||||
#include "etl/impl/LedgerFetcher.hpp"
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "util/log/Logger.hpp"
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
#include <boost/json/object.hpp>
|
||||
#include <grpcpp/grpcpp.h>
|
||||
#include <org/xrpl/rpc/v1/get_ledger.pb.h>
|
||||
@@ -85,7 +84,7 @@ class ETLService {
|
||||
using ExtractorType = etl::impl::Extractor<DataPipeType, LedgerFetcherType>;
|
||||
using LedgerLoaderType = etl::impl::LedgerLoader<LoadBalancerType, LedgerFetcherType>;
|
||||
using LedgerPublisherType = etl::impl::LedgerPublisher<CacheType>;
|
||||
using AmendmentBlockHandlerType = etl::impl::AmendmentBlockHandler<>;
|
||||
using AmendmentBlockHandlerType = etl::impl::AmendmentBlockHandler;
|
||||
using TransformerType =
|
||||
etl::impl::Transformer<DataPipeType, LedgerLoaderType, LedgerPublisherType, AmendmentBlockHandlerType>;
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2023, 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "etl/SystemState.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
namespace etl::impl {
|
||||
|
||||
struct AmendmentBlockAction {
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
static util::Logger const log{"ETL"};
|
||||
LOG(log.fatal()) << "Can't process new ledgers: The current ETL source is not compatible with the version of "
|
||||
<< "the libxrpl Clio is currently using. Please upgrade Clio to a newer version.";
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ActionCallableType = AmendmentBlockAction>
|
||||
class AmendmentBlockHandler {
|
||||
std::reference_wrapper<boost::asio::io_context> ctx_;
|
||||
std::reference_wrapper<SystemState> state_;
|
||||
boost::asio::steady_timer timer_;
|
||||
std::chrono::milliseconds interval_;
|
||||
|
||||
ActionCallableType action_;
|
||||
|
||||
public:
|
||||
template <typename DurationType = std::chrono::seconds>
|
||||
AmendmentBlockHandler(
|
||||
boost::asio::io_context& ioc,
|
||||
SystemState& state,
|
||||
DurationType interval = DurationType{1},
|
||||
ActionCallableType&& action = ActionCallableType()
|
||||
)
|
||||
: ctx_{std::ref(ioc)}
|
||||
, state_{std::ref(state)}
|
||||
, timer_{ioc}
|
||||
, interval_{std::chrono::duration_cast<std::chrono::milliseconds>(interval)}
|
||||
, action_{std::move(action)}
|
||||
{
|
||||
}
|
||||
|
||||
~AmendmentBlockHandler()
|
||||
{
|
||||
boost::asio::post(ctx_.get(), [this]() { timer_.cancel(); });
|
||||
}
|
||||
|
||||
void
|
||||
onAmendmentBlock()
|
||||
{
|
||||
state_.get().isAmendmentBlocked = true;
|
||||
startReportingTimer();
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
startReportingTimer()
|
||||
{
|
||||
action_();
|
||||
|
||||
timer_.expires_after(interval_);
|
||||
timer_.async_wait([this](auto ec) {
|
||||
if (!ec)
|
||||
boost::asio::post(ctx_.get(), [this] { startReportingTimer(); });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace etl::impl
|
||||
56
src/etl/impl/AmendmentBlockHandler.cpp
Normal file
56
src/etl/impl/AmendmentBlockHandler.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, 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 "etl/impl/AmendmentBlockHandler.hpp"
|
||||
|
||||
#include "etl/SystemState.hpp"
|
||||
#include "util/log/Logger.hpp"
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace etl::impl {
|
||||
|
||||
AmendmentBlockHandler::ActionType const AmendmentBlockHandler::defaultAmendmentBlockAction = []() {
|
||||
static util::Logger const log{"ETL"};
|
||||
LOG(log.fatal()) << "Can't process new ledgers: The current ETL source is not compatible with the version of "
|
||||
<< "the libxrpl Clio is currently using. Please upgrade Clio to a newer version.";
|
||||
};
|
||||
|
||||
AmendmentBlockHandler::AmendmentBlockHandler(
|
||||
boost::asio::io_context& ioc,
|
||||
SystemState& state,
|
||||
std::chrono::steady_clock::duration interval,
|
||||
ActionType action
|
||||
)
|
||||
: state_{std::ref(state)}, repeat_{ioc}, interval_{interval}, action_{std::move(action)}
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AmendmentBlockHandler::onAmendmentBlock()
|
||||
{
|
||||
state_.get().isAmendmentBlocked = true;
|
||||
repeat_.start(interval_, action_);
|
||||
}
|
||||
|
||||
} // namespace etl::impl
|
||||
59
src/etl/impl/AmendmentBlockHandler.hpp
Normal file
59
src/etl/impl/AmendmentBlockHandler.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2023, 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "etl/SystemState.hpp"
|
||||
#include "util/Repeat.hpp"
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
namespace etl::impl {
|
||||
|
||||
class AmendmentBlockHandler {
|
||||
public:
|
||||
using ActionType = std::function<void()>;
|
||||
|
||||
private:
|
||||
std::reference_wrapper<SystemState> state_;
|
||||
util::Repeat repeat_;
|
||||
std::chrono::steady_clock::duration interval_;
|
||||
|
||||
ActionType action_;
|
||||
|
||||
public:
|
||||
static ActionType const defaultAmendmentBlockAction;
|
||||
|
||||
AmendmentBlockHandler(
|
||||
boost::asio::io_context& ioc,
|
||||
SystemState& state,
|
||||
std::chrono::steady_clock::duration interval = std::chrono::seconds{1},
|
||||
ActionType action = defaultAmendmentBlockAction
|
||||
);
|
||||
|
||||
void
|
||||
onAmendmentBlock();
|
||||
};
|
||||
|
||||
} // namespace etl::impl
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "data/DBHelpers.hpp"
|
||||
#include "data/Types.hpp"
|
||||
#include "etl/SystemState.hpp"
|
||||
#include "etl/impl/AmendmentBlock.hpp"
|
||||
#include "etl/impl/AmendmentBlockHandler.hpp"
|
||||
#include "etl/impl/LedgerLoader.hpp"
|
||||
#include "util/Assert.hpp"
|
||||
#include "util/LedgerUtils.hpp"
|
||||
|
||||
@@ -14,11 +14,12 @@ target_sources(
|
||||
prometheus/Prometheus.cpp
|
||||
Random.cpp
|
||||
Retry.cpp
|
||||
SignalsHandler.cpp
|
||||
Repeat.cpp
|
||||
requests/RequestBuilder.cpp
|
||||
requests/Types.cpp
|
||||
requests/WsConnection.cpp
|
||||
requests/impl/SslContext.cpp
|
||||
SignalsHandler.cpp
|
||||
Taggable.cpp
|
||||
TerminationHandler.cpp
|
||||
TimeUtils.cpp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2023, the clio developers.
|
||||
Copyright (c) 2024, 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
|
||||
@@ -17,17 +17,22 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
#include "util/Repeat.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
struct FakeAmendmentBlockAction {
|
||||
std::reference_wrapper<std::size_t> callCount;
|
||||
namespace util {
|
||||
|
||||
void
|
||||
operator()() const
|
||||
{
|
||||
++(callCount.get());
|
||||
}
|
||||
};
|
||||
Repeat::Repeat(boost::asio::io_context& ioc) : timer_(ioc)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Repeat::stop()
|
||||
{
|
||||
stopping_ = true;
|
||||
timer_.cancel();
|
||||
semaphore_.acquire();
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
90
src/util/Repeat.hpp
Normal file
90
src/util/Repeat.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/post.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <concepts>
|
||||
#include <semaphore>
|
||||
|
||||
namespace util {
|
||||
|
||||
/**
|
||||
* @brief A class to repeat some action at a regular interval
|
||||
* @note io_context must be stopped before the Repeat object is destroyed. Otherwise it is undefined behavior
|
||||
*/
|
||||
class Repeat {
|
||||
boost::asio::steady_timer timer_;
|
||||
std::atomic_bool stopping_{false};
|
||||
std::binary_semaphore semaphore_{0};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new Repeat object
|
||||
*
|
||||
* @param ioc The io_context to use
|
||||
*/
|
||||
Repeat(boost::asio::io_context& ioc);
|
||||
|
||||
/**
|
||||
* @brief Stop repeating
|
||||
* @note This method will block to ensure the repeating is actually stopped. But blocking time should be very short.
|
||||
*/
|
||||
void
|
||||
stop();
|
||||
|
||||
/**
|
||||
* @brief Start asynchronously repeating
|
||||
*
|
||||
* @tparam Action The action type
|
||||
* @param interval The interval to repeat
|
||||
* @param action The action to call regularly
|
||||
*/
|
||||
template <std::invocable Action>
|
||||
void
|
||||
start(std::chrono::steady_clock::duration interval, Action&& action)
|
||||
{
|
||||
stopping_ = false;
|
||||
startImpl(interval, std::forward<Action>(action));
|
||||
}
|
||||
|
||||
private:
|
||||
template <std::invocable Action>
|
||||
void
|
||||
startImpl(std::chrono::steady_clock::duration interval, Action&& action)
|
||||
{
|
||||
timer_.expires_after(interval);
|
||||
timer_.async_wait([this, interval, action = std::forward<Action>(action)](auto const&) mutable {
|
||||
if (stopping_) {
|
||||
semaphore_.release();
|
||||
return;
|
||||
}
|
||||
action();
|
||||
|
||||
startImpl(interval, std::forward<Action>(action));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
@@ -57,11 +57,6 @@ Retry::Retry(RetryStrategyPtr strategy, boost::asio::strand<boost::asio::io_cont
|
||||
{
|
||||
}
|
||||
|
||||
Retry::~Retry()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
void
|
||||
Retry::cancel()
|
||||
{
|
||||
|
||||
@@ -89,7 +89,6 @@ public:
|
||||
* @param strand The strand to use for async operations
|
||||
*/
|
||||
Retry(RetryStrategyPtr strategy, boost::asio::strand<boost::asio::io_context::executor_type> strand);
|
||||
~Retry();
|
||||
|
||||
/**
|
||||
* @brief Schedule a retry
|
||||
|
||||
@@ -34,11 +34,6 @@ public:
|
||||
timer_.async_wait(std::forward<decltype(fn)>(fn));
|
||||
}
|
||||
|
||||
~SteadyTimer()
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
SteadyTimer(SteadyTimer&&) = default;
|
||||
|
||||
SteadyTimer(SteadyTimer const&) = delete;
|
||||
|
||||
@@ -150,24 +150,15 @@ LogService::init(util::Config const& config)
|
||||
|
||||
// get default severity, can be overridden per channel using the `log_channels` array
|
||||
auto defaultSeverity = config.valueOr<Severity>("log_level", Severity::NFO);
|
||||
static constexpr std::array<char const*, 7> channels = {
|
||||
"General",
|
||||
"WebServer",
|
||||
"Backend",
|
||||
"RPC",
|
||||
"ETL",
|
||||
"Subscriptions",
|
||||
"Performance",
|
||||
};
|
||||
|
||||
std::unordered_map<std::string, Severity> min_severity;
|
||||
for (auto const& channel : channels)
|
||||
for (auto const& channel : Logger::CHANNELS)
|
||||
min_severity[channel] = defaultSeverity;
|
||||
min_severity["Alert"] = Severity::WRN; // Channel for alerts, always warning severity
|
||||
|
||||
for (auto const overrides = config.arrayOr("log_channels", {}); auto const& cfg : overrides) {
|
||||
auto name = cfg.valueOrThrow<std::string>("channel", "Channel name is required");
|
||||
if (std::count(std::begin(channels), std::end(channels), name) == 0)
|
||||
if (std::count(std::begin(Logger::CHANNELS), std::end(Logger::CHANNELS), name) == 0)
|
||||
throw std::runtime_error("Can't override settings for log channel " + name + ": invalid channel");
|
||||
|
||||
min_severity[name] = cfg.valueOr<Severity>("log_level", defaultSeverity);
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/formatter_parser.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
@@ -176,6 +177,16 @@ class Logger final {
|
||||
};
|
||||
|
||||
public:
|
||||
static constexpr std::array<char const*, 7> CHANNELS = {
|
||||
"General",
|
||||
"WebServer",
|
||||
"Backend",
|
||||
"RPC",
|
||||
"ETL",
|
||||
"Subscriptions",
|
||||
"Performance",
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Construct a new Logger object that produces loglines for the
|
||||
* specified channel.
|
||||
|
||||
@@ -58,9 +58,8 @@ public:
|
||||
* @brief A simple denial of service guard used for rate limiting.
|
||||
*
|
||||
* @tparam WhitelistHandlerType The type of the whitelist handler
|
||||
* @tparam SweepHandlerType The type of the sweep handler
|
||||
*/
|
||||
template <typename WhitelistHandlerType, typename SweepHandlerType>
|
||||
template <typename WhitelistHandlerType>
|
||||
class BasicDOSGuard : public BaseDOSGuard {
|
||||
/**
|
||||
* @brief Accumulated state per IP, state will be reset accordingly
|
||||
@@ -90,19 +89,13 @@ public:
|
||||
*
|
||||
* @param config Clio config
|
||||
* @param whitelistHandler Whitelist handler that checks whitelist for IP addresses
|
||||
* @param sweepHandler Sweep handler that implements the sweeping behaviour
|
||||
*/
|
||||
BasicDOSGuard(
|
||||
util::Config const& config,
|
||||
WhitelistHandlerType const& whitelistHandler,
|
||||
SweepHandlerType& sweepHandler
|
||||
)
|
||||
BasicDOSGuard(util::Config const& config, WhitelistHandlerType const& whitelistHandler)
|
||||
: whitelistHandler_{std::cref(whitelistHandler)}
|
||||
, maxFetches_{config.valueOr("dos_guard.max_fetches", DEFAULT_MAX_FETCHES)}
|
||||
, maxConnCount_{config.valueOr("dos_guard.max_connections", DEFAULT_MAX_CONNECTIONS)}
|
||||
, maxRequestCount_{config.valueOr("dos_guard.max_requests", DEFAULT_MAX_REQUESTS)}
|
||||
{
|
||||
sweepHandler.setup(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,6 +255,6 @@ private:
|
||||
/**
|
||||
* @brief A simple denial of service guard used for rate limiting.
|
||||
*/
|
||||
using DOSGuard = BasicDOSGuard<web::WhitelistHandler, web::IntervalSweepHandler>;
|
||||
using DOSGuard = BasicDOSGuard<web::WhitelistHandler>;
|
||||
|
||||
} // namespace web
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
#include "web/IntervalSweepHandler.hpp"
|
||||
|
||||
#include "util/Assert.hpp"
|
||||
#include "util/Constants.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
|
||||
@@ -30,49 +28,22 @@
|
||||
#include <boost/system/detail/error_code.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
namespace web {
|
||||
|
||||
IntervalSweepHandler::IntervalSweepHandler(util::Config const& config, boost::asio::io_context& ctx)
|
||||
: sweepInterval_{std::max(
|
||||
1u,
|
||||
static_cast<uint32_t>(
|
||||
config.valueOr("dos_guard.sweep_interval", 1.0) * static_cast<double>(util::MILLISECONDS_PER_SECOND)
|
||||
)
|
||||
)}
|
||||
, ctx_{std::ref(ctx)}
|
||||
, timer_{ctx.get_executor()}
|
||||
IntervalSweepHandler::IntervalSweepHandler(
|
||||
util::Config const& config,
|
||||
boost::asio::io_context& ctx,
|
||||
web::BaseDOSGuard& dosGuard
|
||||
)
|
||||
: repeat_{std::ref(ctx)}
|
||||
{
|
||||
}
|
||||
|
||||
IntervalSweepHandler::~IntervalSweepHandler()
|
||||
{
|
||||
boost::asio::post(ctx_.get(), [this]() { timer_.cancel(); });
|
||||
}
|
||||
|
||||
void
|
||||
IntervalSweepHandler::setup(web::BaseDOSGuard* guard)
|
||||
{
|
||||
ASSERT(dosGuard_ == nullptr, "Cannot setup DOS guard more than once");
|
||||
dosGuard_ = guard;
|
||||
ASSERT(dosGuard_ != nullptr, "DOS guard must be not null");
|
||||
|
||||
createTimer();
|
||||
}
|
||||
|
||||
void
|
||||
IntervalSweepHandler::createTimer()
|
||||
{
|
||||
timer_.expires_after(sweepInterval_);
|
||||
timer_.async_wait([this](boost::system::error_code const& error) {
|
||||
if (error == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
dosGuard_->clear();
|
||||
boost::asio::post(ctx_.get(), [this] { createTimer(); });
|
||||
});
|
||||
auto const sweepInterval{std::max(
|
||||
std::chrono::milliseconds{1u}, util::Config::toMilliseconds(config.valueOr("dos_guard.sweep_interval", 1.0))
|
||||
)};
|
||||
repeat_.start(sweepInterval, [&dosGuard] { dosGuard.clear(); });
|
||||
}
|
||||
|
||||
} // namespace web
|
||||
|
||||
@@ -19,28 +19,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "util/Repeat.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/steady_timer.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
namespace web {
|
||||
|
||||
class BaseDOSGuard;
|
||||
|
||||
/**
|
||||
* @brief Sweep handler using a steady_timer and boost::asio::io_context.
|
||||
* @brief Sweep handler clearing context every sweep interval from config.
|
||||
*/
|
||||
class IntervalSweepHandler {
|
||||
std::chrono::milliseconds sweepInterval_;
|
||||
std::reference_wrapper<boost::asio::io_context> ctx_;
|
||||
boost::asio::steady_timer timer_;
|
||||
|
||||
web::BaseDOSGuard* dosGuard_ = nullptr;
|
||||
util::Repeat repeat_;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -48,25 +40,9 @@ public:
|
||||
*
|
||||
* @param config Clio config to use
|
||||
* @param ctx The boost::asio::io_context to use
|
||||
* @param dosGuard The DOS guard to use
|
||||
*/
|
||||
IntervalSweepHandler(util::Config const& config, boost::asio::io_context& ctx);
|
||||
|
||||
/**
|
||||
* @brief Cancels the sweep timer.
|
||||
*/
|
||||
~IntervalSweepHandler();
|
||||
|
||||
/**
|
||||
* @brief This setup member function is called by @ref BasicDOSGuard during its initialization.
|
||||
*
|
||||
* @param guard Pointer to the dos guard
|
||||
*/
|
||||
void
|
||||
setup(web::BaseDOSGuard* guard);
|
||||
|
||||
private:
|
||||
void
|
||||
createTimer();
|
||||
IntervalSweepHandler(util::Config const& config, boost::asio::io_context& ctx, web::BaseDOSGuard& dosGuard);
|
||||
};
|
||||
|
||||
} // namespace web
|
||||
|
||||
@@ -2,7 +2,7 @@ add_library(clio_testing_common)
|
||||
|
||||
target_sources(
|
||||
clio_testing_common PRIVATE util/StringUtils.cpp util/TestHttpServer.cpp util/TestWsServer.cpp util/TestObject.cpp
|
||||
util/AssignRandomPort.cpp
|
||||
util/AssignRandomPort.cpp util/WithTimeout.cpp
|
||||
)
|
||||
|
||||
include(deps/gtest)
|
||||
|
||||
@@ -171,14 +171,4 @@ struct HandlerWithoutInputMock {
|
||||
MOCK_METHOD(Result, process, (rpc::Context const&), (const));
|
||||
};
|
||||
|
||||
// testing sweep handler by mocking dos guard
|
||||
template <typename SweepHandler>
|
||||
struct BasicDOSGuardMock : public web::BaseDOSGuard {
|
||||
BasicDOSGuardMock(SweepHandler& handler)
|
||||
{
|
||||
handler.setup(this);
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, clear, (), (noexcept, override));
|
||||
};
|
||||
} // namespace tests::common
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
@@ -97,6 +98,13 @@ struct SyncAsioContextTest : virtual public NoLoggerFixture {
|
||||
ctx.reset();
|
||||
}
|
||||
|
||||
void
|
||||
runContextFor(std::chrono::milliseconds duration)
|
||||
{
|
||||
ctx.run_for(duration);
|
||||
ctx.reset();
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::io_context ctx;
|
||||
};
|
||||
|
||||
@@ -22,10 +22,14 @@
|
||||
#include "util/log/Logger.hpp"
|
||||
|
||||
#include <boost/log/core/core.hpp>
|
||||
#include <boost/log/expressions/predicates/channel_severity_filter.hpp>
|
||||
#include <boost/log/keywords/format.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/formatter_parser.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
@@ -70,8 +74,14 @@ public:
|
||||
core->remove_all_sinks();
|
||||
boost::log::add_console_log(stream_, keywords::format = "%Channel%:%Severity% %Message%");
|
||||
auto min_severity = expr::channel_severity_filter(util::log_channel, util::log_severity);
|
||||
|
||||
std::ranges::for_each(util::Logger::CHANNELS, [&min_severity](char const* channel) {
|
||||
min_severity[channel] = util::Severity::TRC;
|
||||
});
|
||||
|
||||
min_severity["General"] = util::Severity::DBG;
|
||||
min_severity["Trace"] = util::Severity::TRC;
|
||||
|
||||
core->set_filter(min_severity);
|
||||
core->set_logging_enabled(true);
|
||||
}
|
||||
@@ -89,6 +99,12 @@ protected:
|
||||
{
|
||||
ASSERT_TRUE(buffer_.getStrAndReset().empty());
|
||||
}
|
||||
|
||||
std::string
|
||||
getLoggerString()
|
||||
{
|
||||
return buffer_.getStrAndReset();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
47
tests/common/util/WithTimeout.cpp
Normal file
47
tests/common/util/WithTimeout.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, 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/WithTimeout.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
namespace tests::common::util {
|
||||
|
||||
void
|
||||
withTimeout(std::chrono::steady_clock::duration timeout, std::function<void()> function)
|
||||
{
|
||||
std::promise<void> promise;
|
||||
auto future = promise.get_future();
|
||||
std::thread t([&promise, &function] {
|
||||
function();
|
||||
promise.set_value();
|
||||
});
|
||||
if (future.wait_for(timeout) == std::future_status::timeout) {
|
||||
FAIL() << "Timeout " << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count() << "ms exceeded";
|
||||
}
|
||||
t.join();
|
||||
}
|
||||
|
||||
} // namespace tests::common::util
|
||||
36
tests/common/util/WithTimeout.hpp
Normal file
36
tests/common/util/WithTimeout.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, 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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
|
||||
namespace tests::common::util {
|
||||
|
||||
/**
|
||||
* @brief Run a function with a timeout. If the function does not complete within the timeout, the test will fail.
|
||||
*
|
||||
* @param timeout The timeout duration
|
||||
* @param function The function to run
|
||||
*/
|
||||
void
|
||||
withTimeout(std::chrono::steady_clock::duration timeout, std::function<void()> function);
|
||||
|
||||
} // namespace tests::common::util
|
||||
@@ -121,6 +121,7 @@ target_sources(
|
||||
util/requests/WsConnectionTests.cpp
|
||||
util/RandomTests.cpp
|
||||
util/RetryTests.cpp
|
||||
util/RepeatTests.cpp
|
||||
util/SignalsHandlerTests.cpp
|
||||
util/TimeUtilsTests.cpp
|
||||
util/TxUtilTests.cpp
|
||||
@@ -129,7 +130,7 @@ target_sources(
|
||||
web/impl/ServerSslContextTests.cpp
|
||||
web/RPCServerHandlerTests.cpp
|
||||
web/ServerTests.cpp
|
||||
web/SweepHandlerTests.cpp
|
||||
web/IntervalSweepHandlerTests.cpp
|
||||
web/WhitelistHandlerTests.cpp
|
||||
# New Config
|
||||
util/newconfig/ArrayViewTests.cpp
|
||||
|
||||
@@ -38,7 +38,6 @@ constexpr auto JSONData = R"JSON(
|
||||
{
|
||||
"dos_guard": {
|
||||
"max_fetches": 100,
|
||||
"sweep_interval": 1,
|
||||
"max_connections": 2,
|
||||
"max_requests": 3,
|
||||
"whitelist": [
|
||||
@@ -55,33 +54,13 @@ struct MockWhitelistHandler {
|
||||
};
|
||||
|
||||
using MockWhitelistHandlerType = NiceMock<MockWhitelistHandler>;
|
||||
|
||||
class FakeSweepHandler {
|
||||
private:
|
||||
using guardType = BasicDOSGuard<MockWhitelistHandlerType, FakeSweepHandler>;
|
||||
guardType* dosGuard_;
|
||||
|
||||
public:
|
||||
void
|
||||
setup(guardType* guard)
|
||||
{
|
||||
dosGuard_ = guard;
|
||||
}
|
||||
|
||||
void
|
||||
sweep()
|
||||
{
|
||||
dosGuard_->clear();
|
||||
}
|
||||
};
|
||||
}; // namespace
|
||||
|
||||
class DOSGuardTest : public NoLoggerFixture {
|
||||
protected:
|
||||
Config cfg{json::parse(JSONData)};
|
||||
FakeSweepHandler sweepHandler{};
|
||||
MockWhitelistHandlerType whitelistHandler;
|
||||
BasicDOSGuard<MockWhitelistHandlerType, FakeSweepHandler> guard{cfg, whitelistHandler, sweepHandler};
|
||||
BasicDOSGuard<MockWhitelistHandlerType> guard{cfg, whitelistHandler};
|
||||
};
|
||||
|
||||
TEST_F(DOSGuardTest, Whitelisting)
|
||||
@@ -124,7 +103,7 @@ TEST_F(DOSGuardTest, ClearFetchCountOnTimer)
|
||||
EXPECT_FALSE(guard.add(IP, 1)); // can't add even 1 anymore
|
||||
EXPECT_FALSE(guard.isOk(IP));
|
||||
|
||||
sweepHandler.sweep(); // pretend sweep called from timer
|
||||
guard.clear(); // pretend sweep called from timer
|
||||
EXPECT_TRUE(guard.isOk(IP)); // can fetch again
|
||||
}
|
||||
|
||||
@@ -148,6 +127,6 @@ TEST_F(DOSGuardTest, RequestLimitOnTimer)
|
||||
EXPECT_TRUE(guard.isOk(IP));
|
||||
EXPECT_FALSE(guard.request(IP));
|
||||
EXPECT_FALSE(guard.isOk(IP));
|
||||
sweepHandler.sweep();
|
||||
guard.clear();
|
||||
EXPECT_TRUE(guard.isOk(IP)); // can request again
|
||||
}
|
||||
|
||||
@@ -18,37 +18,42 @@
|
||||
//==============================================================================
|
||||
|
||||
#include "etl/SystemState.hpp"
|
||||
#include "etl/impl/AmendmentBlock.hpp"
|
||||
#include "util/FakeAmendmentBlockAction.hpp"
|
||||
#include "etl/impl/AmendmentBlockHandler.hpp"
|
||||
#include "util/AsioContextTestFixture.hpp"
|
||||
#include "util/LoggerFixtures.hpp"
|
||||
#include "util/MockPrometheus.hpp"
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
|
||||
using namespace testing;
|
||||
using namespace etl;
|
||||
using namespace etl::impl;
|
||||
|
||||
struct AmendmentBlockHandlerTest : util::prometheus::WithPrometheus, NoLoggerFixture {
|
||||
using AmendmentBlockHandlerType = impl::AmendmentBlockHandler<FakeAmendmentBlockAction>;
|
||||
|
||||
boost::asio::io_context ioc_;
|
||||
struct AmendmentBlockHandlerTest : util::prometheus::WithPrometheus, SyncAsioContextTest {
|
||||
testing::StrictMock<testing::MockFunction<void()>> actionMock;
|
||||
etl::SystemState state;
|
||||
};
|
||||
|
||||
TEST_F(AmendmentBlockHandlerTest, CallToOnAmendmentBlockSetsStateAndRepeatedlyCallsAction)
|
||||
{
|
||||
std::size_t callCount = 0;
|
||||
SystemState state;
|
||||
AmendmentBlockHandlerType handler{ioc_, state, std::chrono::nanoseconds{1}, {std::ref(callCount)}};
|
||||
AmendmentBlockHandler handler{ctx, state, std::chrono::nanoseconds{1}, actionMock.AsStdFunction()};
|
||||
|
||||
EXPECT_FALSE(state.isAmendmentBlocked);
|
||||
EXPECT_CALL(actionMock, Call()).Times(testing::AtLeast(10));
|
||||
handler.onAmendmentBlock();
|
||||
EXPECT_TRUE(state.isAmendmentBlocked);
|
||||
|
||||
ioc_.run_for(std::chrono::milliseconds{1});
|
||||
EXPECT_TRUE(callCount >= 10);
|
||||
runContextFor(std::chrono::milliseconds{1});
|
||||
}
|
||||
|
||||
struct DefaultAmendmentBlockActionTest : LoggerFixture {};
|
||||
|
||||
TEST_F(DefaultAmendmentBlockActionTest, Call)
|
||||
{
|
||||
AmendmentBlockHandler::defaultAmendmentBlockAction();
|
||||
auto const loggerString = getLoggerString();
|
||||
EXPECT_TRUE(loggerString.starts_with("ETL:FTL Can't process new ledgers")) << "LoggerString " << loggerString;
|
||||
}
|
||||
|
||||
82
tests/unit/util/RepeatTests.cpp
Normal file
82
tests/unit/util/RepeatTests.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, 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/AsioContextTestFixture.hpp"
|
||||
#include "util/Repeat.hpp"
|
||||
#include "util/WithTimeout.hpp"
|
||||
|
||||
#include <boost/asio/error.hpp>
|
||||
#include <boost/asio/executor_work_guard.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <ranges>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
using namespace util;
|
||||
using testing::AtLeast;
|
||||
|
||||
struct RepeatTests : SyncAsioContextTest {
|
||||
Repeat repeat{ctx};
|
||||
testing::StrictMock<testing::MockFunction<void()>> handlerMock;
|
||||
|
||||
void
|
||||
withRunningContext(std::function<void()> func)
|
||||
{
|
||||
tests::common::util::withTimeout(std::chrono::seconds{1000}, [this, func = std::move(func)]() {
|
||||
auto workGuard = boost::asio::make_work_guard(ctx);
|
||||
std::thread thread{[this]() { ctx.run(); }};
|
||||
func();
|
||||
workGuard.reset();
|
||||
thread.join();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(RepeatTests, CallsHandler)
|
||||
{
|
||||
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
|
||||
EXPECT_CALL(handlerMock, Call).Times(AtLeast(10));
|
||||
runContextFor(std::chrono::milliseconds{20});
|
||||
}
|
||||
|
||||
TEST_F(RepeatTests, StopsOnStop)
|
||||
{
|
||||
withRunningContext([this]() {
|
||||
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
|
||||
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{10});
|
||||
repeat.stop();
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(RepeatTests, RunsAfterStop)
|
||||
{
|
||||
withRunningContext([this]() {
|
||||
for ([[maybe_unused]] auto _ : std::ranges::iota_view(0, 2)) {
|
||||
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
|
||||
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{10});
|
||||
repeat.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -17,9 +17,9 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "rpc/FakesAndMocks.hpp"
|
||||
#include "util/AsioContextTestFixture.hpp"
|
||||
#include "util/config/Config.hpp"
|
||||
#include "web/DOSGuard.hpp"
|
||||
#include "web/IntervalSweepHandler.hpp"
|
||||
|
||||
#include <boost/json/parse.hpp>
|
||||
@@ -28,30 +28,29 @@
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace util;
|
||||
using namespace web;
|
||||
using namespace testing;
|
||||
|
||||
constexpr static auto JSONData = R"JSON(
|
||||
struct IntervalSweepHandlerTest : SyncAsioContextTest {
|
||||
protected:
|
||||
constexpr static auto JSONData = R"JSON(
|
||||
{
|
||||
"dos_guard": {
|
||||
"max_fetches": 100,
|
||||
"sweep_interval": 0.1,
|
||||
"max_connections": 2,
|
||||
"whitelist": ["127.0.0.1"]
|
||||
"sweep_interval": 0
|
||||
}
|
||||
}
|
||||
)JSON";
|
||||
|
||||
class DOSGuardIntervalSweepHandlerTest : public SyncAsioContextTest {
|
||||
protected:
|
||||
Config cfg{boost::json::parse(JSONData)};
|
||||
IntervalSweepHandler sweepHandler{cfg, ctx};
|
||||
tests::common::BasicDOSGuardMock<IntervalSweepHandler> guard{sweepHandler};
|
||||
struct DosGuardMock : BaseDOSGuard {
|
||||
MOCK_METHOD(void, clear, (), (noexcept, override));
|
||||
};
|
||||
testing::StrictMock<DosGuardMock> guardMock;
|
||||
|
||||
util::Config cfg{boost::json::parse(JSONData)};
|
||||
IntervalSweepHandler sweepHandler{cfg, ctx, guardMock};
|
||||
};
|
||||
|
||||
TEST_F(DOSGuardIntervalSweepHandlerTest, SweepAfterInterval)
|
||||
TEST_F(IntervalSweepHandlerTest, SweepAfterInterval)
|
||||
{
|
||||
EXPECT_CALL(guard, clear()).Times(AtLeast(2));
|
||||
ctx.run_for(std::chrono::milliseconds(400));
|
||||
EXPECT_CALL(guardMock, clear()).Times(testing::AtLeast(10));
|
||||
runContextFor(std::chrono::milliseconds{20});
|
||||
}
|
||||
@@ -127,14 +127,14 @@ struct WebServerTest : NoLoggerFixture {
|
||||
boost::asio::io_context ctxSync;
|
||||
std::string const port = std::to_string(tests::util::generateFreePort());
|
||||
Config cfg{generateJSONWithDynamicPort(port)};
|
||||
IntervalSweepHandler sweepHandler = web::IntervalSweepHandler{cfg, ctxSync};
|
||||
WhitelistHandler whitelistHandler = web::WhitelistHandler{cfg};
|
||||
DOSGuard dosGuard = web::DOSGuard{cfg, whitelistHandler, sweepHandler};
|
||||
WhitelistHandler whitelistHandler{cfg};
|
||||
DOSGuard dosGuard{cfg, whitelistHandler};
|
||||
IntervalSweepHandler sweepHandler{cfg, ctxSync, dosGuard};
|
||||
|
||||
Config cfgOverload{generateJSONDataOverload(port)};
|
||||
IntervalSweepHandler sweepHandlerOverload = web::IntervalSweepHandler{cfgOverload, ctxSync};
|
||||
WhitelistHandler whitelistHandlerOverload = web::WhitelistHandler{cfgOverload};
|
||||
DOSGuard dosGuardOverload = web::DOSGuard{cfgOverload, whitelistHandlerOverload, sweepHandlerOverload};
|
||||
WhitelistHandler whitelistHandlerOverload{cfgOverload};
|
||||
DOSGuard dosGuardOverload{cfgOverload, whitelistHandlerOverload};
|
||||
IntervalSweepHandler sweepHandlerOverload{cfgOverload, ctxSync, dosGuardOverload};
|
||||
// this ctx is for http server
|
||||
boost::asio::io_context ctx;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user