libp2p_ping/
lib.rs

1// Copyright 2017-2018 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21//! This module implements the `/ipfs/ping/1.0.0` protocol.
22//!
23//! The ping protocol can be used as a simple application-layer health check
24//! for connections of any [`Transport`] as well as to measure and record
25//! round-trip times.
26//!
27//! # Usage
28//!
29//! The [`Behaviour`] struct implements the [`NetworkBehaviour`] trait.
30//! It will respond to inbound ping requests and periodically send outbound ping requests on every established connection.
31//!
32//! It is up to the user to implement a health-check / connection management policy based on the ping protocol.
33//!
34//! For example:
35//!
36//! - Disconnect from peers with an RTT > 200ms
37//! - Disconnect from peers which don't support the ping protocol
38//! - Disconnect from peers upon the first ping failure
39//!
40//! Users should inspect emitted [`Event`]s and call APIs on [`Swarm`]:
41//!
42//! - [`Swarm::close_connection`](libp2p_swarm::Swarm::close_connection) to close a specific connection
43//! - [`Swarm::disconnect_peer_id`](libp2p_swarm::Swarm::disconnect_peer_id) to close all connections to a peer
44//!
45//! [`Swarm`]: libp2p_swarm::Swarm
46//! [`Transport`]: libp2p_core::Transport
47
48#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
49
50mod handler;
51mod protocol;
52
53use handler::Handler;
54use libp2p_core::transport::PortUse;
55use libp2p_core::{Endpoint, Multiaddr};
56use libp2p_identity::PeerId;
57use libp2p_swarm::{
58    behaviour::FromSwarm, ConnectionDenied, ConnectionId, NetworkBehaviour, THandler,
59    THandlerInEvent, THandlerOutEvent, ToSwarm,
60};
61use std::time::Duration;
62use std::{
63    collections::VecDeque,
64    task::{Context, Poll},
65};
66
67pub use self::protocol::PROTOCOL_NAME;
68pub use handler::{Config, Failure};
69
70/// A [`NetworkBehaviour`] that responds to inbound pings and
71/// periodically sends outbound pings on every established connection.
72///
73/// See the crate root documentation for more information.
74pub struct Behaviour {
75    /// Configuration for outbound pings.
76    config: Config,
77    /// Queue of events to yield to the swarm.
78    events: VecDeque<Event>,
79}
80
81/// Event generated by the `Ping` network behaviour.
82#[derive(Debug)]
83pub struct Event {
84    /// The peer ID of the remote.
85    pub peer: PeerId,
86    /// The connection the ping was executed on.
87    pub connection: ConnectionId,
88    /// The result of an inbound or outbound ping.
89    pub result: Result<Duration, Failure>,
90}
91
92impl Behaviour {
93    /// Creates a new `Ping` network behaviour with the given configuration.
94    pub fn new(config: Config) -> Self {
95        Self {
96            config,
97            events: VecDeque::new(),
98        }
99    }
100}
101
102impl Default for Behaviour {
103    fn default() -> Self {
104        Self::new(Config::new())
105    }
106}
107
108impl NetworkBehaviour for Behaviour {
109    type ConnectionHandler = Handler;
110    type ToSwarm = Event;
111
112    fn handle_established_inbound_connection(
113        &mut self,
114        _: ConnectionId,
115        _: PeerId,
116        _: &Multiaddr,
117        _: &Multiaddr,
118    ) -> Result<THandler<Self>, ConnectionDenied> {
119        Ok(Handler::new(self.config.clone()))
120    }
121
122    fn handle_established_outbound_connection(
123        &mut self,
124        _: ConnectionId,
125        _: PeerId,
126        _: &Multiaddr,
127        _: Endpoint,
128        _: PortUse,
129    ) -> Result<THandler<Self>, ConnectionDenied> {
130        Ok(Handler::new(self.config.clone()))
131    }
132
133    fn on_connection_handler_event(
134        &mut self,
135        peer: PeerId,
136        connection: ConnectionId,
137        result: THandlerOutEvent<Self>,
138    ) {
139        self.events.push_front(Event {
140            peer,
141            connection,
142            result,
143        })
144    }
145
146    #[tracing::instrument(level = "trace", name = "NetworkBehaviour::poll", skip(self))]
147    fn poll(&mut self, _: &mut Context<'_>) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
148        if let Some(e) = self.events.pop_back() {
149            Poll::Ready(ToSwarm::GenerateEvent(e))
150        } else {
151            Poll::Pending
152        }
153    }
154
155    fn on_swarm_event(&mut self, _event: FromSwarm) {}
156}