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::{Endpoint, Multiaddr};
55use libp2p_identity::PeerId;
56use libp2p_swarm::{
57 behaviour::FromSwarm, ConnectionDenied, ConnectionId, NetworkBehaviour, PollParameters,
58 THandler, THandlerInEvent, THandlerOutEvent, ToSwarm,
59};
60use std::time::Duration;
61use std::{
62 collections::VecDeque,
63 task::{Context, Poll},
64};
65
66pub use self::protocol::PROTOCOL_NAME;
67pub use handler::{Config, Failure};
68
69/// A [`NetworkBehaviour`] that responds to inbound pings and
70/// periodically sends outbound pings on every established connection.
71///
72/// See the crate root documentation for more information.
73pub struct Behaviour {
74 /// Configuration for outbound pings.
75 config: Config,
76 /// Queue of events to yield to the swarm.
77 events: VecDeque<Event>,
78}
79
80/// Event generated by the `Ping` network behaviour.
81#[derive(Debug)]
82pub struct Event {
83 /// The peer ID of the remote.
84 pub peer: PeerId,
85 /// The connection the ping was executed on.
86 pub connection: ConnectionId,
87 /// The result of an inbound or outbound ping.
88 pub result: Result<Duration, Failure>,
89}
90
91impl Behaviour {
92 /// Creates a new `Ping` network behaviour with the given configuration.
93 pub fn new(config: Config) -> Self {
94 Self {
95 config,
96 events: VecDeque::new(),
97 }
98 }
99}
100
101impl Default for Behaviour {
102 fn default() -> Self {
103 Self::new(Config::new())
104 }
105}
106
107impl NetworkBehaviour for Behaviour {
108 type ConnectionHandler = Handler;
109 type ToSwarm = Event;
110
111 fn handle_established_inbound_connection(
112 &mut self,
113 _: ConnectionId,
114 peer: PeerId,
115 _: &Multiaddr,
116 _: &Multiaddr,
117 ) -> Result<THandler<Self>, ConnectionDenied> {
118 Ok(Handler::new(self.config.clone(), peer))
119 }
120
121 fn handle_established_outbound_connection(
122 &mut self,
123 _: ConnectionId,
124 peer: PeerId,
125 _: &Multiaddr,
126 _: Endpoint,
127 ) -> Result<THandler<Self>, ConnectionDenied> {
128 Ok(Handler::new(self.config.clone(), peer))
129 }
130
131 fn on_connection_handler_event(
132 &mut self,
133 peer: PeerId,
134 connection: ConnectionId,
135 result: THandlerOutEvent<Self>,
136 ) {
137 self.events.push_front(Event {
138 peer,
139 connection,
140 result,
141 })
142 }
143
144 fn poll(
145 &mut self,
146 _: &mut Context<'_>,
147 _: &mut impl PollParameters,
148 ) -> Poll<ToSwarm<Self::ToSwarm, THandlerInEvent<Self>>> {
149 if let Some(e) = self.events.pop_back() {
150 Poll::Ready(ToSwarm::GenerateEvent(e))
151 } else {
152 Poll::Pending
153 }
154 }
155
156 fn on_swarm_event(&mut self, event: FromSwarm<Self::ConnectionHandler>) {
157 match event {
158 FromSwarm::ConnectionEstablished(_)
159 | FromSwarm::ConnectionClosed(_)
160 | FromSwarm::AddressChange(_)
161 | FromSwarm::DialFailure(_)
162 | FromSwarm::ListenFailure(_)
163 | FromSwarm::NewListener(_)
164 | FromSwarm::NewListenAddr(_)
165 | FromSwarm::ExpiredListenAddr(_)
166 | FromSwarm::ListenerError(_)
167 | FromSwarm::ListenerClosed(_)
168 | FromSwarm::NewExternalAddrCandidate(_)
169 | FromSwarm::ExternalAddrExpired(_)
170 | FromSwarm::ExternalAddrConfirmed(_) => {}
171 }
172 }
173}