use crate::transport::{ConnectionInfo, IListener, ITransport, ListenerEvent, TransportListener};
use crate::{transport::TransportError, Multiaddr, Transport};
use async_trait::async_trait;
use futures::future::{select, Either};
use futures_timer::Delay;
use log::trace;
use std::time::Duration;
#[derive(Debug, Clone)]
pub struct TransportTimeout<InnerTrans> {
inner: InnerTrans,
outgoing_timeout: Duration,
incoming_timeout: Duration,
}
impl<InnerTrans> TransportTimeout<InnerTrans> {
pub fn new(trans: InnerTrans, timeout: Duration) -> Self {
TransportTimeout {
inner: trans,
outgoing_timeout: timeout,
incoming_timeout: timeout,
}
}
pub fn with_outgoing_timeout(trans: InnerTrans, timeout: Duration) -> Self {
TransportTimeout {
inner: trans,
outgoing_timeout: timeout,
incoming_timeout: Duration::from_secs(100 * 365 * 24 * 3600), }
}
pub fn with_ingoing_timeout(trans: InnerTrans, timeout: Duration) -> Self {
TransportTimeout {
inner: trans,
outgoing_timeout: Duration::from_secs(100 * 365 * 24 * 3600), incoming_timeout: timeout,
}
}
}
#[async_trait]
impl<InnerTrans> Transport for TransportTimeout<InnerTrans>
where
InnerTrans: Transport + Clone + 'static,
InnerTrans::Output: ConnectionInfo + 'static,
{
type Output = InnerTrans::Output;
fn listen_on(&mut self, addr: Multiaddr) -> Result<IListener<Self::Output>, TransportError> {
let listener = self.inner.listen_on(addr)?;
let listener = TimeoutListener {
inner: listener,
timeout: self.incoming_timeout,
};
Ok(Box::new(listener))
}
async fn dial(&mut self, addr: Multiaddr) -> Result<Self::Output, TransportError> {
let output = select(self.inner.dial(addr), Delay::new(self.outgoing_timeout)).await;
match output {
Either::Left((stream, _)) => {
trace!("dialing connected first");
Ok(stream?)
}
Either::Right(_) => {
trace!("dialing timeout first");
Err(TransportError::Timeout)
}
}
}
fn box_clone(&self) -> ITransport<Self::Output> {
Box::new(self.clone())
}
fn protocols(&self) -> Vec<u32> {
self.inner.protocols()
}
}
pub struct TimeoutListener<TOutput> {
inner: IListener<TOutput>,
timeout: Duration,
}
#[async_trait]
impl<TOutput: Send> TransportListener for TimeoutListener<TOutput> {
type Output = TOutput;
async fn accept(&mut self) -> Result<ListenerEvent<Self::Output>, TransportError> {
let output = select(self.inner.accept(), Delay::new(self.timeout)).await;
match output {
Either::Left((r, _)) => {
trace!("accepted first");
r
}
Either::Right(_) => {
trace!("accept timeout first");
Err(TransportError::Timeout)
}
}
}
fn multi_addr(&self) -> Option<&Multiaddr> {
self.inner.multi_addr()
}
}
#[cfg(test)]
mod tests {
use crate::transport::memory::MemoryTransport;
use crate::{Multiaddr, Transport};
use libp2prs_runtime::task;
use std::time::Duration;
#[test]
fn dialer_and_listener_timeout() {
fn test1(addr: Multiaddr) {
task::block_on(async move {
let mut timeout_listener = MemoryTransport::default().timeout(Duration::from_secs(1)).listen_on(addr).unwrap();
assert!(timeout_listener.accept().await.is_err());
});
}
fn test2(addr: Multiaddr) {
task::block_on(async move {
let mut tcp = MemoryTransport::default().timeout(Duration::from_secs(1));
assert!(tcp.dial(addr.clone()).await.is_err());
});
}
test1("/memory/1111".parse().unwrap());
test2("/memory/1111".parse().unwrap());
}
}