// Copyright 2023 Protocol Labs. // // 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. use std::{ collections::VecDeque, task::{Context, Poll}, time::{Duration, Instant}, }; use futures::{future::BoxFuture, stream::FuturesUnordered, FutureExt, StreamExt, TryFutureExt}; use libp2p_core::upgrade::{DeniedUpgrade, ReadyUpgrade}; use libp2p_swarm::{ handler::{ ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, ListenUpgradeError, }, ConnectionHandler, ConnectionHandlerEvent, KeepAlive, StreamProtocol, StreamUpgradeError, SubstreamProtocol, }; use void::Void; use super::{RunId, RunParams, RunStats}; #[derive(Debug)] pub struct Command { pub(crate) id: RunId, pub(crate) params: RunParams, } #[derive(Debug)] pub struct Event { pub(crate) id: RunId, pub(crate) result: Result>, } pub struct Handler { /// Queue of events to return when polled. queued_events: VecDeque< ConnectionHandlerEvent< ::OutboundProtocol, ::OutboundOpenInfo, ::OutEvent, ::Error, >, >, requested_streams: VecDeque, outbound: FuturesUnordered>>, keep_alive: KeepAlive, } impl Handler { pub fn new() -> Self { Self { queued_events: Default::default(), requested_streams: Default::default(), outbound: Default::default(), keep_alive: KeepAlive::Yes, } } } impl Default for Handler { fn default() -> Self { Self::new() } } impl ConnectionHandler for Handler { type InEvent = Command; type OutEvent = Event; type Error = Void; type InboundProtocol = DeniedUpgrade; type OutboundProtocol = ReadyUpgrade; type OutboundOpenInfo = (); type InboundOpenInfo = (); fn listen_protocol(&self) -> SubstreamProtocol { SubstreamProtocol::new(DeniedUpgrade, ()) } fn on_behaviour_event(&mut self, command: Self::InEvent) { self.requested_streams.push_back(command); self.queued_events .push_back(ConnectionHandlerEvent::OutboundSubstreamRequest { protocol: SubstreamProtocol::new(ReadyUpgrade::new(crate::PROTOCOL_NAME), ()), }) } fn on_connection_event( &mut self, event: ConnectionEvent< Self::InboundProtocol, Self::OutboundProtocol, Self::InboundOpenInfo, Self::OutboundOpenInfo, >, ) { match event { ConnectionEvent::FullyNegotiatedInbound(FullyNegotiatedInbound { protocol, .. }) => void::unreachable(protocol), ConnectionEvent::FullyNegotiatedOutbound(FullyNegotiatedOutbound { protocol, info: (), }) => { let Command { id, params } = self .requested_streams .pop_front() .expect("opened a stream without a pending command"); self.outbound.push( crate::protocol::send_receive(params, protocol) .map_ok(move |timers| Event { id, result: Ok(RunStats { params, timers }), }) .boxed(), ); } ConnectionEvent::AddressChange(_) => {} ConnectionEvent::DialUpgradeError(DialUpgradeError { info: (), error }) => { let Command { id, .. } = self .requested_streams .pop_front() .expect("requested stream without pending command"); self.queued_events .push_back(ConnectionHandlerEvent::Custom(Event { id, result: Err(error), })); } ConnectionEvent::ListenUpgradeError(ListenUpgradeError { info: (), error }) => { void::unreachable(error) } } } fn connection_keep_alive(&self) -> KeepAlive { self.keep_alive } fn poll( &mut self, cx: &mut Context<'_>, ) -> Poll< ConnectionHandlerEvent< Self::OutboundProtocol, Self::OutboundOpenInfo, Self::OutEvent, Self::Error, >, > { // Return queued events. if let Some(event) = self.queued_events.pop_front() { return Poll::Ready(event); } while let Poll::Ready(Some(result)) = self.outbound.poll_next_unpin(cx) { match result { Ok(event) => return Poll::Ready(ConnectionHandlerEvent::Custom(event)), Err(e) => { panic!("{e:?}") } } } if self.outbound.is_empty() { match self.keep_alive { KeepAlive::Yes => { self.keep_alive = KeepAlive::Until(Instant::now() + Duration::from_secs(10)); } KeepAlive::Until(_) => {} KeepAlive::No => panic!("Handler never sets KeepAlive::No."), } } else { self.keep_alive = KeepAlive::Yes } Poll::Pending } }