// Copyright 2017-2018 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //! Handles the `/ipfs/ping/1.0.0` protocol. This allows pinging a remote node and waiting for an //! answer. //! //! # Usage //! //! Create a `Ping` struct, which implements the `ConnectionUpgrade` trait. When used as a //! connection upgrade, it will produce a tuple of type `(Pinger, impl Future)` which //! are named the *pinger* and the *ponger*. //! //! The *pinger* has a method named `ping` which will send a ping to the remote, while the *ponger* //! is a future that will process the data received on the socket and will be signalled only when //! the connection closes. //! //! # About timeouts //! //! For technical reasons, this crate doesn't handle timeouts. The action of pinging returns a //! future that is signalled only when the remote answers. If the remote is not responsive, the //! future will never be signalled. //! //! For implementation reasons, resources allocated for a ping are only ever fully reclaimed after //! a pong has been received by the remote. Therefore if you repeatedly ping a non-responsive //! remote you will end up using more and more memory (albeit the amount is very very small every //! time), even if you destroy the future returned by `ping`. //! //! This is probably not a problem in practice, because the nature of the ping protocol is to //! determine whether a remote is still alive, and any reasonable user of this crate will close //! connections to non-responsive remotes. //! //! # Example //! //! ```no_run //! extern crate futures; //! extern crate libp2p_ping; //! extern crate libp2p_core; //! extern crate libp2p_tcp; //! extern crate tokio; //! //! use futures::{Future, Stream}; //! use libp2p_core::{transport::Transport, upgrade::apply_outbound}; //! use libp2p_ping::protocol::Ping; //! use tokio::runtime::current_thread::Runtime; //! //! # fn main() { //! let ping_dialer = libp2p_tcp::TcpConfig::new() //! .and_then(|socket, _| { //! apply_outbound(socket, Ping::default()).map_err(|e| e.into_io_error()) //! }) //! .dial("/ip4/127.0.0.1/tcp/12345".parse::().unwrap()).unwrap_or_else(|_| panic!()) //! .and_then(|mut pinger| { //! pinger.ping(()); //! let f = pinger.into_future() //! .map(|_| println!("received pong")) //! .map_err(|(e, _)| e); //! Box::new(f) as Box + Send> //! }); //! //! // Runs until the ping arrives. //! let mut rt = Runtime::new().unwrap(); //! let _ = rt.block_on(ping_dialer).unwrap(); //! # } //! ``` //! pub mod dial_handler; pub mod listen_handler; pub mod protocol; use futures::prelude::*; use libp2p_core::either::EitherOutput; use libp2p_core::swarm::{ConnectedPoint, NetworkBehaviour, NetworkBehaviourAction, PollParameters}; use libp2p_core::{protocols_handler::ProtocolsHandler, protocols_handler::ProtocolsHandlerSelect, PeerId}; use std::{marker::PhantomData, time::Duration}; use tokio_io::{AsyncRead, AsyncWrite}; /// Network behaviour that handles receiving pings sent by other nodes and periodically pings the /// nodes we connect to. pub struct Ping { /// Marker to pin the generics. marker: PhantomData, /// Queue of events to report to the user. events: Vec, } /// Event generated by the `Ping` behaviour. pub enum PingEvent { /// We have successfully pinged a peer we are connected to. PingSuccess { /// Id of the peer that we pinged. peer: PeerId, /// Time elapsed between when we sent the ping and when we received the response. time: Duration, } } impl Ping { /// Creates a `Ping`. pub fn new() -> Self { Ping { marker: PhantomData, events: Vec::new(), } } } impl Default for Ping { #[inline] fn default() -> Self { Ping::new() } } impl NetworkBehaviour for Ping where TSubstream: AsyncRead + AsyncWrite, { type ProtocolsHandler = ProtocolsHandlerSelect, dial_handler::PeriodicPingHandler>; type OutEvent = PingEvent; fn new_handler(&mut self) -> Self::ProtocolsHandler { listen_handler::PingListenHandler::new() .select(dial_handler::PeriodicPingHandler::new()) } fn inject_connected(&mut self, _: PeerId, _: ConnectedPoint) {} fn inject_disconnected(&mut self, _: &PeerId, _: ConnectedPoint) {} fn inject_node_event( &mut self, source: PeerId, event: ::OutEvent, ) { match event { EitherOutput::Second(dial_handler::OutEvent::PingSuccess(time)) => { self.events.push(PingEvent::PingSuccess { peer: source, time, }) }, _ => () } } fn poll( &mut self, _: &mut PollParameters, ) -> Async< NetworkBehaviourAction< ::InEvent, Self::OutEvent, >, > { if !self.events.is_empty() { return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0))); } Async::NotReady } }