Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

396 lines
13 KiB
Rust
Raw Normal View History

// Copyright 2019 Parity Technologies (UK) Ltd.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
use crate::{protocol, PROTOCOL_NAME};
use futures::future::{BoxFuture, Either};
use futures::prelude::*;
use futures_timer::Delay;
use libp2p_core::upgrade::ReadyUpgrade;
use libp2p_identity::PeerId;
use libp2p_swarm::handler::{
ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound,
};
use libp2p_swarm::{
ConnectionHandler, ConnectionHandlerEvent, KeepAlive, Stream, StreamProtocol,
StreamUpgradeError, SubstreamProtocol,
2019-04-16 15:57:29 +02:00
};
use std::collections::VecDeque;
use std::{
error::Error,
fmt, io,
task::{Context, Poll},
time::Duration,
};
2019-04-16 15:57:29 +02:00
use void::Void;
2019-04-16 15:57:29 +02:00
/// The configuration for outbound pings.
#[derive(Debug, Clone)]
pub struct Config {
2019-04-16 15:57:29 +02:00
/// The timeout of an outbound ping.
timeout: Duration,
/// The duration between outbound pings.
2019-04-16 15:57:29 +02:00
interval: Duration,
}
impl Config {
/// Creates a new [`Config`] with the following default settings:
2019-04-16 15:57:29 +02:00
///
/// * [`Config::with_interval`] 15s
/// * [`Config::with_timeout`] 20s
2019-04-16 15:57:29 +02:00
///
/// These settings have the following effect:
///
/// * A ping is sent every 15 seconds on a healthy connection.
/// * Every ping sent must yield a response within 20 seconds in order to
/// be successful.
pub fn new() -> Self {
Self {
timeout: Duration::from_secs(20),
interval: Duration::from_secs(15),
}
}
/// Sets the ping timeout.
pub fn with_timeout(mut self, d: Duration) -> Self {
self.timeout = d;
self
}
/// Sets the ping interval.
pub fn with_interval(mut self, d: Duration) -> Self {
self.interval = d;
self
}
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
2019-04-16 15:57:29 +02:00
/// An outbound ping failure.
#[derive(Debug)]
pub enum Failure {
2019-04-16 15:57:29 +02:00
/// The ping timed out, i.e. no response was received within the
/// configured ping timeout.
Timeout,
/// The peer does not support the ping protocol.
Unsupported,
2019-04-16 15:57:29 +02:00
/// The ping failed for reasons other than a timeout.
Other {
error: Box<dyn std::error::Error + Send + 'static>,
},
}
impl Failure {
fn other(e: impl std::error::Error + Send + 'static) -> Self {
Self::Other { error: Box::new(e) }
}
}
impl fmt::Display for Failure {
2020-07-27 20:27:33 +00:00
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
match self {
Failure::Timeout => f.write_str("Ping timeout"),
Failure::Other { error } => write!(f, "Ping error: {error}"),
Failure::Unsupported => write!(f, "Ping protocol not supported"),
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
}
}
}
impl Error for Failure {
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Failure::Timeout => None,
Failure::Other { error } => Some(&**error),
Failure::Unsupported => None,
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
}
}
}
2019-04-16 15:57:29 +02:00
/// Protocol handler that handles pinging the remote at a regular period
/// and answering ping queries.
pub struct Handler {
2019-04-16 15:57:29 +02:00
/// Configuration options.
config: Config,
/// The timer used for the delay to the next ping.
interval: Delay,
/// Outbound ping failures that are pending to be processed by `poll()`.
pending_errors: VecDeque<Failure>,
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
/// The number of consecutive ping failures that occurred.
///
/// Each successful ping resets this counter to 0.
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
failures: u32,
/// The outbound ping state.
outbound: Option<OutboundState>,
/// The inbound pong handler, i.e. if there is an inbound
/// substream, this is always a future that waits for the
/// next inbound ping to be answered.
inbound: Option<PongFuture>,
/// Tracks the state of our handler.
state: State,
/// The peer we are connected to.
peer: PeerId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State {
/// We are inactive because the other peer doesn't support ping.
Inactive {
/// Whether or not we've reported the missing support yet.
///
/// This is used to avoid repeated events being emitted for a specific connection.
reported: bool,
},
/// We are actively pinging the other peer.
Active,
}
impl Handler {
/// Builds a new [`Handler`] with the given configuration.
pub fn new(config: Config, peer: PeerId) -> Self {
Handler {
peer,
2019-04-16 15:57:29 +02:00
config,
interval: Delay::new(Duration::new(0, 0)),
pending_errors: VecDeque::with_capacity(2),
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
failures: 0,
outbound: None,
inbound: None,
state: State::Active,
}
}
fn on_dial_upgrade_error(
&mut self,
DialUpgradeError { error, .. }: DialUpgradeError<
<Self as ConnectionHandler>::OutboundOpenInfo,
<Self as ConnectionHandler>::OutboundProtocol,
>,
) {
self.outbound = None; // Request a new substream on the next `poll`.
let error = match error {
StreamUpgradeError::NegotiationFailed => {
debug_assert_eq!(self.state, State::Active);
self.state = State::Inactive { reported: false };
return;
}
// Note: This timeout only covers protocol negotiation.
StreamUpgradeError::Timeout => Failure::Other {
error: Box::new(std::io::Error::new(
std::io::ErrorKind::TimedOut,
"ping protocol negotiation timed out",
)),
},
StreamUpgradeError::Apply(e) => void::unreachable(e),
StreamUpgradeError::Io(e) => Failure::Other { error: Box::new(e) },
};
self.pending_errors.push_front(error);
}
}
impl ConnectionHandler for Handler {
type FromBehaviour = Void;
type ToBehaviour = Result<Duration, Failure>;
type Error = Void;
type InboundProtocol = ReadyUpgrade<StreamProtocol>;
type OutboundProtocol = ReadyUpgrade<StreamProtocol>;
type OutboundOpenInfo = ();
type InboundOpenInfo = ();
fn listen_protocol(&self) -> SubstreamProtocol<ReadyUpgrade<StreamProtocol>, ()> {
SubstreamProtocol::new(ReadyUpgrade::new(PROTOCOL_NAME), ())
}
fn on_behaviour_event(&mut self, _: Void) {}
fn connection_keep_alive(&self) -> KeepAlive {
KeepAlive::No
}
2020-07-27 20:27:33 +00:00
fn poll(
&mut self,
cx: &mut Context<'_>,
) -> Poll<
ConnectionHandlerEvent<
ReadyUpgrade<StreamProtocol>,
(),
Result<Duration, Failure>,
Self::Error,
>,
> {
match self.state {
State::Inactive { reported: true } => {
return Poll::Pending; // nothing to do on this connection
}
State::Inactive { reported: false } => {
self.state = State::Inactive { reported: true };
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err(
Failure::Unsupported,
)));
}
State::Active => {}
}
// Respond to inbound pings.
if let Some(fut) = self.inbound.as_mut() {
match fut.poll_unpin(cx) {
Poll::Pending => {}
Poll::Ready(Err(e)) => {
log::debug!("Inbound ping error: {:?}", e);
self.inbound = None;
}
Poll::Ready(Ok(stream)) => {
log::trace!("answered inbound ping from {}", self.peer);
// A ping from a remote peer has been answered, wait for the next.
self.inbound = Some(protocol::recv_ping(stream).boxed());
2019-04-16 15:57:29 +02:00
}
}
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
}
loop {
// Check for outbound ping failures.
if let Some(error) = self.pending_errors.pop_back() {
log::debug!("Ping failure: {:?}", error);
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
self.failures += 1;
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
// Note: For backward-compatibility the first failure is always "free"
// and silent. This allows peers who use a new substream
// for each ping to have successful ping exchanges with peers
// that use a single substream, since every successful ping
// resets `failures` to `0`.
if self.failures > 1 {
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Err(error)));
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
}
}
// Continue outbound pings.
match self.outbound.take() {
Some(OutboundState::Ping(mut ping)) => match ping.poll_unpin(cx) {
Poll::Pending => {
self.outbound = Some(OutboundState::Ping(ping));
break;
}
Poll::Ready(Ok((stream, rtt))) => {
log::debug!("latency to {} is {}ms", self.peer, rtt.as_millis());
self.failures = 0;
self.interval.reset(self.config.interval);
self.outbound = Some(OutboundState::Idle(stream));
return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Ok(rtt)));
}
Poll::Ready(Err(e)) => {
self.pending_errors.push_front(e);
}
},
Some(OutboundState::Idle(stream)) => match self.interval.poll_unpin(cx) {
Poll::Pending => {
self.outbound = Some(OutboundState::Idle(stream));
break;
}
Poll::Ready(()) => {
self.outbound = Some(OutboundState::Ping(
send_ping(stream, self.config.timeout).boxed(),
));
}
},
Some(OutboundState::OpenStream) => {
self.outbound = Some(OutboundState::OpenStream);
break;
}
None => {
self.outbound = Some(OutboundState::OpenStream);
let protocol = SubstreamProtocol::new(ReadyUpgrade::new(PROTOCOL_NAME), ());
return Poll::Ready(ConnectionHandlerEvent::OutboundSubstreamRequest {
protocol,
});
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
}
}
}
Poll::Pending
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
}
fn on_connection_event(
&mut self,
event: ConnectionEvent<
Self::InboundProtocol,
Self::OutboundProtocol,
Self::InboundOpenInfo,
Self::OutboundOpenInfo,
>,
) {
match event {
ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound {
protocol: stream,
..
}) => {
self.inbound = Some(protocol::recv_ping(stream).boxed());
}
ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound {
protocol: stream,
..
}) => {
self.outbound = Some(OutboundState::Ping(
send_ping(stream, self.config.timeout).boxed(),
));
}
ConnectionEvent::DialUpgradeError(dial_upgrade_error) => {
self.on_dial_upgrade_error(dial_upgrade_error)
}
ConnectionEvent::AddressChange(_)
| ConnectionEvent::ListenUpgradeError(_)
| ConnectionEvent::LocalProtocolsChange(_)
| ConnectionEvent::RemoteProtocolsChange(_) => {}
}
}
}
Remove libp2p-ping keep-alive functionality. (#1067) * Fix connection & handler shutdown when using `KeepAlive::Now`. Delay::new(Instant::now()) is never immediately ready, resulting in `KeepAlive::Now` to have no effect, since the delay is re-created on every execution of `poll()` in the `NodeHandlerWrapper`. It can also send the node handler into a busy-loop, since every newly created Delay will trigger a task wakeup, which creates a new Delay with Instant::now(), and so forth. The use of `Delay::new(Instant::now())` for "immediate" connection shutdown is therefore removed here entirely. An important assumption is thereby that as long as the node handler non-empty `negotiating_in` and `negotiating_out`, the handler is not dependent on such a Delay for task wakeup. * Correction to the libp2p-ping connection timeout. The current connection timeout is always short of one `interval`, because the "countdown" begins with the last received or sent pong (depending on the policy). In effect, the current default config has a connection timeout of 5 seconds (20 - 15) from the point when a ping is sent. Instead, the "countdown" of the connection timeout should always begin with the next scheduled ping. That also makes all configurations valid, avoiding pitfalls. The important properties of the ping handler are now checked to hold for all configurations, in particular: * The next ping must be scheduled no earlier than the ping interval and no later than the connection timeout. * The "countdown" for the connection timeout starts on the next ping, i.e. the full connection timeout remains at the instant when the next ping is sent. * Do not keep connections alive. The ping protocol is not supposed to keep otherwise idle connections alive, only to add an additional condition for terminating them in the form of a configurable number of consecutive failed ping requests. In this context, the `PingPolicy` does not seem useful any longer.
2019-04-20 16:16:31 +02:00
type PingFuture = BoxFuture<'static, Result<(Stream, Duration), Failure>>;
type PongFuture = BoxFuture<'static, Result<Stream, io::Error>>;
/// The current state w.r.t. outbound pings.
enum OutboundState {
/// A new substream is being negotiated for the ping protocol.
OpenStream,
/// The substream is idle, waiting to send the next ping.
Idle(Stream),
/// A ping is being sent and the response awaited.
Ping(PingFuture),
}
/// A wrapper around [`protocol::send_ping`] that enforces a time out.
async fn send_ping(stream: Stream, timeout: Duration) -> Result<(Stream, Duration), Failure> {
let ping = protocol::send_ping(stream);
futures::pin_mut!(ping);
match future::select(ping, Delay::new(timeout)).await {
Either::Left((Ok((stream, rtt)), _)) => Ok((stream, rtt)),
Either::Left((Err(e), _)) => Err(Failure::other(e)),
Either::Right(((), _)) => Err(Failure::Timeout),
}
}