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}