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
31//! established connection.
32//!
33//! It is up to the user to implement a health-check / connection management policy based on the
34//! ping protocol.
35//!
36//! For example:
37//!
38//! - Disconnect from peers with an RTT > 200ms
39//! - Disconnect from peers which don't support the ping protocol
40//! - Disconnect from peers upon the first ping failure
41//!
42//! Users should inspect emitted [`Event`]s and call APIs on [`Swarm`]:
43//!
44//! - [`Swarm::close_connection`](libp2p_swarm::Swarm::close_connection) to close a specific
45//!   connection
46//! - [`Swarm::disconnect_peer_id`](libp2p_swarm::Swarm::disconnect_peer_id) to close all
47//!   connections to a peer
48//!
49//! [`Swarm`]: libp2p_swarm::Swarm
50//! [`Transport`]: libp2p_core::Transport
51
52#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
53
54mod handler;
55mod protocol;
56
57use std::{
58    collections::VecDeque,
59    task::{Context, Poll},
60    time::Duration,
61};
62
63use handler::Handler;
64pub use handler::{Config, Failure};
65use libp2p_core::{transport::PortUse, Endpoint, Multiaddr};
66use libp2p_identity::PeerId;
67use libp2p_swarm::{
68    behaviour::FromSwarm, ConnectionDenied, ConnectionId, NetworkBehaviour, THandler,
69    THandlerInEvent, THandlerOutEvent, ToSwarm,
70};
71
72pub use self::protocol::PROTOCOL_NAME;
73
74/// A [`NetworkBehaviour`] that responds to inbound pings and
75/// periodically sends outbound pings on every established connection.
76///
77/// See the crate root documentation for more information.
78pub struct Behaviour {
79    /// Configuration for outbound pings.
80    config: Config,
81    /// Queue of events to yield to the swarm.
82    events: VecDeque<Event>,
83}
84
85/// Event generated by the `Ping` network behaviour.
86#[derive(Debug)]
87pub struct Event {
88    /// The peer ID of the remote.
89    pub peer: PeerId,
90    /// The connection the ping was executed on.
91    pub connection: ConnectionId,
92    /// The result of an inbound or outbound ping.
93    pub result: Result<Duration, Failure>,
94}
95
96impl Behaviour {
97    /// Creates a new `Ping` network behaviour with the given configuration.
98    pub fn new(config: Config) -> Self {
99        Self {
100            config,
101            events: VecDeque::new(),
102        }
103    }
104}
105
106impl Default for Behaviour {
107    fn default() -> Self {
108        Self::new(Config::new())
109    }
110}
111
112impl NetworkBehaviour for Behaviour {
113    type ConnectionHandler = Handler;
114    type ToSwarm = Event;
115
116    fn handle_established_inbound_connection(
117        &mut self,
118        _: ConnectionId,
119        _: PeerId,
120        _: &Multiaddr,
121        _: &Multiaddr,
122    ) -> Result<THandler<Self>, ConnectionDenied> {
123        Ok(Handler::new(self.config.clone()))
124    }
125
126    fn handle_established_outbound_connection(
127        &mut self,
128        _: ConnectionId,
129        _: PeerId,
130        _: &Multiaddr,
131        _: Endpoint,
132        _: PortUse,
133    ) -> Result<THandler<Self>, ConnectionDenied> {
134        Ok(Handler::new(self.config.clone()))
135    }
136
137    fn on_connection_handler_event(
138        &mut self,
139        peer: PeerId,
140        connection: ConnectionId,
141        result: THandlerOutEvent<Self>,
142    ) {
143        self.events.push_front(Event {
144            peer,
145            connection,
146            result,
147        })
148    }
149
150    #[tracing::instrument(level = "trace", name = "NetworkBehaviour::poll", skip(self))]
151    fn poll(&mut self, _: &mut Context<'_>) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
152        if let Some(e) = self.events.pop_back() {
153            Poll::Ready(ToSwarm::GenerateEvent(e))
154        } else {
155            Poll::Pending
156        }
157    }
158
159    fn on_swarm_event(&mut self, _event: FromSwarm) {}
160}