use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use tokio::time::{Instant, Sleep, sleep};
const FIRST_BYTE_TIMEOUT: Duration = Duration::from_secs(10);
const SILENCE_TIMEOUT: Duration = Duration::from_secs(60);
pub(crate) struct Silenced<I> {
io: I,
deadline: Pin<Box<Sleep>>,
}
impl<I> Silenced<I> {
pub(crate) fn new(io: I) -> Self {
Self {
io,
deadline: Box::pin(sleep(FIRST_BYTE_TIMEOUT)),
}
}
fn heard(&mut self) {
self.deadline
.as_mut()
.reset(Instant::now() + SILENCE_TIMEOUT);
}
fn expired(&mut self, context: &mut Context<'_>) -> bool {
self.deadline.as_mut().poll(context).is_ready()
}
fn written(
&mut self,
context: &mut Context<'_>,
written: Poll<io::Result<usize>>,
) -> Poll<io::Result<usize>> {
match written {
Poll::Ready(Ok(bytes)) if bytes > 0 => {
self.heard();
Poll::Ready(Ok(bytes))
}
Poll::Pending if self.expired(context) => Poll::Ready(Err(io::Error::new(
io::ErrorKind::TimedOut,
"the client read nothing for too long",
))),
other => other,
}
}
}
impl<I: AsyncRead + Unpin> AsyncRead for Silenced<I> {
fn poll_read(
self: Pin<&mut Self>,
context: &mut Context<'_>,
buffer: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
let this = self.get_mut();
let before = buffer.filled().len();
match Pin::new(&mut this.io).poll_read(context, buffer) {
Poll::Ready(Ok(())) if buffer.filled().len() > before => {
this.heard();
Poll::Ready(Ok(()))
}
Poll::Pending if this.expired(context) => Poll::Ready(Ok(())),
other => other,
}
}
}
impl<I: AsyncWrite + Unpin> AsyncWrite for Silenced<I> {
fn poll_write(
self: Pin<&mut Self>,
context: &mut Context<'_>,
bytes: &[u8],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let written = Pin::new(&mut this.io).poll_write(context, bytes);
this.written(context, written)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
context: &mut Context<'_>,
buffers: &[io::IoSlice<'_>],
) -> Poll<io::Result<usize>> {
let this = self.get_mut();
let written = Pin::new(&mut this.io).poll_write_vectored(context, buffers);
this.written(context, written)
}
fn is_write_vectored(&self) -> bool {
self.io.is_write_vectored()
}
fn poll_flush(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().io).poll_flush(context)
}
fn poll_shutdown(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.get_mut().io).poll_shutdown(context)
}
}