Timeouts silent incoming connections
Timeouts silent incoming connections Adds new stat counters: - tcp_silent_connection_drop - tcp_io_timeout_drop
This commit is contained in:
parent
d41789d577
commit
62fbc459ec
5 changed files with 71 additions and 2 deletions
|
@ -351,6 +351,34 @@ TEST (socket, disabled_max_peers_per_ip)
|
|||
node->stop ();
|
||||
}
|
||||
|
||||
TEST (socket, disconnection_of_silent_connections)
|
||||
{
|
||||
nano::system system;
|
||||
auto node = system.add_node ();
|
||||
auto socket = std::make_shared<nano::socket> (*node);
|
||||
// Classify the socket type as real-time as the disconnections are done only for this connection type.
|
||||
socket->type_set (nano::socket::type_t::realtime);
|
||||
// Silent connections are connections open by external peers that don't contribute with any data.
|
||||
socket->set_silent_connection_tolerance_time (std::chrono::seconds{ 5 });
|
||||
auto bootstrap_endpoint = node->bootstrap.endpoint ();
|
||||
std::atomic<bool> connected{ false };
|
||||
// Opening a connection that will be closed because it remains silent during the tolerance time.
|
||||
socket->async_connect (bootstrap_endpoint, [socket, &connected] (boost::system::error_code const & ec) {
|
||||
ASSERT_FALSE (ec);
|
||||
connected = true;
|
||||
});
|
||||
ASSERT_TIMELY (4s, connected);
|
||||
// Checking the connection was closed.
|
||||
ASSERT_TIMELY (10s, socket->is_closed ());
|
||||
|
||||
auto get_tcp_silent_connection_drops = [&node] () {
|
||||
return node->stats.count (nano::stat::type::tcp, nano::stat::detail::tcp_silent_connection_drop, nano::stat::dir::in);
|
||||
};
|
||||
ASSERT_EQ (1, get_tcp_silent_connection_drops ());
|
||||
|
||||
node->stop ();
|
||||
}
|
||||
|
||||
TEST (socket, drop_policy)
|
||||
{
|
||||
auto node_flags = nano::inactive_node_flag_defaults ();
|
||||
|
|
|
@ -156,6 +156,7 @@ public:
|
|||
request_interval_ms = is_dev_network () ? 20 : 500;
|
||||
cleanup_period = is_dev_network () ? std::chrono::seconds (1) : std::chrono::seconds (60);
|
||||
idle_timeout = is_dev_network () ? cleanup_period * 15 : cleanup_period * 2;
|
||||
silent_connection_tolerance_time = std::chrono::seconds (120);
|
||||
syn_cookie_cutoff = std::chrono::seconds (5);
|
||||
bootstrap_interval = std::chrono::seconds (15 * 60);
|
||||
max_peers_per_ip = is_dev_network () ? 10 : 5;
|
||||
|
@ -189,6 +190,7 @@ public:
|
|||
}
|
||||
/** Default maximum idle time for a socket before it's automatically closed */
|
||||
std::chrono::seconds idle_timeout;
|
||||
std::chrono::seconds silent_connection_tolerance_time;
|
||||
std::chrono::seconds syn_cookie_cutoff;
|
||||
std::chrono::seconds bootstrap_interval;
|
||||
/** Maximum number of peers per IP. It is also the max number of connections per IP */
|
||||
|
|
|
@ -749,5 +749,5 @@ bool nano::bootstrap_server::is_bootstrap_connection ()
|
|||
|
||||
bool nano::bootstrap_server::is_realtime_connection ()
|
||||
{
|
||||
return socket->type () == nano::socket::type_t::realtime || socket->type () == nano::socket::type_t::realtime_response_server;
|
||||
return socket->is_realtime_connection ();
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ nano::socket::socket (nano::node & node_a) :
|
|||
node{ node_a },
|
||||
next_deadline{ std::numeric_limits<uint64_t>::max () },
|
||||
last_completion_time{ 0 },
|
||||
io_timeout{ node_a.config.tcp_io_timeout }
|
||||
last_receive_time{ 0 },
|
||||
io_timeout{ node_a.config.tcp_io_timeout },
|
||||
silent_connection_tolerance_time{ node_a.network_params.network.silent_connection_tolerance_time }
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -58,6 +60,7 @@ void nano::socket::async_read (std::shared_ptr<std::vector<uint8_t>> const & buf
|
|||
[this_l, buffer_a, callback_a] (boost::system::error_code const & ec, std::size_t size_a) {
|
||||
this_l->node.stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::in, size_a);
|
||||
this_l->stop_timer ();
|
||||
this_l->update_last_receive_time ();
|
||||
callback_a (ec, size_a);
|
||||
}));
|
||||
}));
|
||||
|
@ -124,6 +127,11 @@ void nano::socket::stop_timer ()
|
|||
last_completion_time = nano::seconds_since_epoch ();
|
||||
}
|
||||
|
||||
void nano::socket::update_last_receive_time ()
|
||||
{
|
||||
last_receive_time = nano::seconds_since_epoch ();
|
||||
}
|
||||
|
||||
void nano::socket::checkup ()
|
||||
{
|
||||
std::weak_ptr<nano::socket> this_w (shared_from_this ());
|
||||
|
@ -131,7 +139,18 @@ void nano::socket::checkup ()
|
|||
if (auto this_l = this_w.lock ())
|
||||
{
|
||||
uint64_t now (nano::seconds_since_epoch ());
|
||||
auto condition_to_disconnect{ false };
|
||||
if (this_l->is_realtime_connection () && now - this_l->last_receive_time > this_l->silent_connection_tolerance_time.count ())
|
||||
{
|
||||
this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_silent_connection_drop, nano::stat::dir::in);
|
||||
condition_to_disconnect = true;
|
||||
}
|
||||
if (this_l->next_deadline != std::numeric_limits<uint64_t>::max () && now - this_l->last_completion_time > this_l->next_deadline)
|
||||
{
|
||||
this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_io_timeout_drop, nano::stat::dir::in);
|
||||
condition_to_disconnect = true;
|
||||
}
|
||||
if (condition_to_disconnect)
|
||||
{
|
||||
if (this_l->node.config.logging.network_timeout_logging ())
|
||||
{
|
||||
|
@ -164,6 +183,14 @@ void nano::socket::timeout_set (std::chrono::seconds io_timeout_a)
|
|||
io_timeout = io_timeout_a;
|
||||
}
|
||||
|
||||
void nano::socket::set_silent_connection_tolerance_time (std::chrono::seconds tolerance_time_a)
|
||||
{
|
||||
auto this_l (shared_from_this ());
|
||||
boost::asio::dispatch (strand, boost::asio::bind_executor (strand, [this_l, tolerance_time_a] () {
|
||||
this_l->silent_connection_tolerance_time = tolerance_time_a;
|
||||
}));
|
||||
}
|
||||
|
||||
void nano::socket::close ()
|
||||
{
|
||||
auto this_l (shared_from_this ());
|
||||
|
|
|
@ -72,6 +72,7 @@ public:
|
|||
/** This can be called to change the maximum idle time, e.g. based on the type of traffic detected. */
|
||||
void timeout_set (std::chrono::seconds io_timeout_a);
|
||||
void start_timer (std::chrono::seconds deadline_a);
|
||||
void set_silent_connection_tolerance_time (std::chrono::seconds tolerance_time_a);
|
||||
bool max () const
|
||||
{
|
||||
return queue_size >= queue_size_max;
|
||||
|
@ -88,6 +89,14 @@ public:
|
|||
{
|
||||
type_m = type_a;
|
||||
}
|
||||
bool is_realtime_connection ()
|
||||
{
|
||||
return type () == nano::socket::type_t::realtime || type () == nano::socket::type_t::realtime_response_server;
|
||||
}
|
||||
bool is_closed ()
|
||||
{
|
||||
return closed;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** Holds the buffer and callback for queued writes */
|
||||
|
@ -107,8 +116,10 @@ protected:
|
|||
|
||||
std::atomic<uint64_t> next_deadline;
|
||||
std::atomic<uint64_t> last_completion_time;
|
||||
std::atomic<uint64_t> last_receive_time;
|
||||
std::atomic<bool> timed_out{ false };
|
||||
std::atomic<std::chrono::seconds> io_timeout;
|
||||
std::chrono::seconds silent_connection_tolerance_time;
|
||||
std::atomic<std::size_t> queue_size{ 0 };
|
||||
|
||||
/** Set by close() - completion handlers must check this. This is more reliable than checking
|
||||
|
@ -117,6 +128,7 @@ protected:
|
|||
void close_internal ();
|
||||
void start_timer ();
|
||||
void stop_timer ();
|
||||
void update_last_receive_time ();
|
||||
void checkup ();
|
||||
|
||||
private:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue