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}