use std::future::Future;
use std::io::{self, IoSlice};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{ready, Context, Poll};
use std::time::Duration;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::net::TcpStream;
use tokio::time::{sleep, Sleep};
use super::stats::{ApiStats, Rejection};
#[derive(Debug)]
pub struct Stalled;
impl std::fmt::Display for Stalled {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("the client took none of its answer in time")
}
}
impl std::error::Error for Stalled {}
pub struct Socket {
tcp: TcpStream,
stall: Duration,
stats: Arc<ApiStats>,
timer: Option<Pin<Box<Sleep>>>,
waiting: bool,
}
impl Socket {
pub fn new(tcp: TcpStream, stall: Duration, stats: Arc<ApiStats>) -> Socket {
Socket {
tcp,
stall,
stats,
timer: None,
waiting: false,
}
}
fn progress<T>(
&mut self,
cx: &mut Context<'_>,
write: Poll<io::Result<T>>,
) -> Poll<io::Result<T>> {
if write.is_ready() {
self.waiting = false;
return write;
}
let stall = self.stall;
let timer = self.timer.get_or_insert_with(|| Box::pin(sleep(stall)));
if !self.waiting {
self.waiting = true;
timer.as_mut().reset(tokio::time::Instant::now() + stall);
}
ready!(timer.as_mut().poll(cx));
self.stats.rejected.inc(Rejection::Timeout);
tracing::debug!(
peer = ?self.tcp.peer_addr().ok(),
"chain API client took none of its answer in time; closing"
);
Poll::Ready(Err(io::Error::new(io::ErrorKind::TimedOut, Stalled)))
}
}
impl AsyncRead for Socket {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().tcp).poll_read(cx, buf)
}
}
impl AsyncWrite for Socket {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let write = Pin::new(&mut this.tcp).poll_write(cx, buf);
this.progress(cx, write)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let write = Pin::new(&mut this.tcp).poll_write_vectored(cx, bufs);
this.progress(cx, write)
}
fn is_write_vectored(&self) -> bool {
self.tcp.is_write_vectored()
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let this = self.get_mut();
let flush = Pin::new(&mut this.tcp).poll_flush(cx);
this.progress(cx, flush)
}
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().tcp).poll_shutdown(cx)
}
}