ifc_lite_server/
write_timeout.rs1use axum::serve::Listener;
15use std::{
16 future::Future,
17 io::{self, IoSlice},
18 net::SocketAddr,
19 pin::Pin,
20 task::{Context, Poll},
21 time::Duration,
22};
23use tokio::{
24 io::{AsyncRead, AsyncWrite, ReadBuf},
25 net::{TcpListener, TcpStream},
26 time::{sleep, Sleep},
27};
28
29pub(crate) struct WriteTimeoutListener {
30 inner: TcpListener,
31 timeout: Option<Duration>,
32}
33
34impl WriteTimeoutListener {
35 pub(crate) fn new(inner: TcpListener, timeout: Duration) -> Self {
36 Self {
37 inner,
38 timeout: (!timeout.is_zero()).then_some(timeout),
39 }
40 }
41}
42
43impl Listener for WriteTimeoutListener {
44 type Io = WriteTimeoutIo<TcpStream>;
45 type Addr = SocketAddr;
46
47 async fn accept(&mut self) -> (Self::Io, Self::Addr) {
48 let (stream, address) = <TcpListener as Listener>::accept(&mut self.inner).await;
49 (WriteTimeoutIo::new(stream, self.timeout), address)
50 }
51
52 fn local_addr(&self) -> io::Result<Self::Addr> {
53 self.inner.local_addr()
54 }
55}
56
57pub(crate) struct WriteTimeoutIo<T> {
58 inner: Option<T>,
59 timeout: Option<Duration>,
60 deadline: Option<Pin<Box<Sleep>>>,
61}
62
63impl<T> WriteTimeoutIo<T> {
64 fn new(inner: T, timeout: Option<Duration>) -> Self {
65 Self {
66 inner: Some(inner),
67 timeout,
68 deadline: None,
69 }
70 }
71
72 fn disconnected() -> io::Error {
73 io::Error::new(io::ErrorKind::NotConnected, "connection already closed")
74 }
75
76 fn poll_stall(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
77 let Some(timeout) = self.timeout else {
78 return Poll::Pending;
79 };
80 let deadline = self
81 .deadline
82 .get_or_insert_with(|| Box::pin(sleep(timeout)));
83 if deadline.as_mut().poll(cx).is_pending() {
84 return Poll::Pending;
85 }
86 self.inner.take();
87 Poll::Ready(Err(io::Error::new(
88 io::ErrorKind::TimedOut,
89 "socket write made no progress before IFC_STREAM_IDLE_TIMEOUT_SECS",
90 )))
91 }
92
93 fn note_progress(&mut self) {
94 self.deadline = None;
95 }
96}
97
98impl<T: AsyncRead + Unpin> AsyncRead for WriteTimeoutIo<T> {
99 fn poll_read(
100 mut self: Pin<&mut Self>,
101 cx: &mut Context<'_>,
102 buffer: &mut ReadBuf<'_>,
103 ) -> Poll<io::Result<()>> {
104 let Some(inner) = self.inner.as_mut() else {
105 return Poll::Ready(Err(Self::disconnected()));
106 };
107 Pin::new(inner).poll_read(cx, buffer)
108 }
109}
110
111impl<T: AsyncWrite + Unpin> AsyncWrite for WriteTimeoutIo<T> {
112 fn poll_write(
113 mut self: Pin<&mut Self>,
114 cx: &mut Context<'_>,
115 buffer: &[u8],
116 ) -> Poll<io::Result<usize>> {
117 let Some(inner) = self.inner.as_mut() else {
118 return Poll::Ready(Err(Self::disconnected()));
119 };
120 match Pin::new(inner).poll_write(cx, buffer) {
121 Poll::Ready(Ok(written)) => {
122 if written > 0 || buffer.is_empty() {
123 self.note_progress();
124 }
125 Poll::Ready(Ok(written))
126 }
127 Poll::Ready(Err(error)) => Poll::Ready(Err(error)),
128 Poll::Pending => self.poll_stall(cx),
129 }
130 }
131
132 fn poll_write_vectored(
133 mut self: Pin<&mut Self>,
134 cx: &mut Context<'_>,
135 buffers: &[IoSlice<'_>],
136 ) -> Poll<io::Result<usize>> {
137 let all_empty = buffers.iter().all(|buffer| buffer.is_empty());
138 let Some(inner) = self.inner.as_mut() else {
139 return Poll::Ready(Err(Self::disconnected()));
140 };
141 match Pin::new(inner).poll_write_vectored(cx, buffers) {
142 Poll::Ready(Ok(written)) => {
143 if written > 0 || all_empty {
144 self.note_progress();
145 }
146 Poll::Ready(Ok(written))
147 }
148 Poll::Ready(Err(error)) => Poll::Ready(Err(error)),
149 Poll::Pending => self.poll_stall(cx),
150 }
151 }
152
153 fn is_write_vectored(&self) -> bool {
154 self.inner
155 .as_ref()
156 .is_some_and(AsyncWrite::is_write_vectored)
157 }
158
159 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
160 let Some(inner) = self.inner.as_mut() else {
161 return Poll::Ready(Err(Self::disconnected()));
162 };
163 match Pin::new(inner).poll_flush(cx) {
164 Poll::Ready(result) => {
165 self.note_progress();
166 Poll::Ready(result)
167 }
168 Poll::Pending => self.poll_stall(cx).map_ok(|_| ()),
169 }
170 }
171
172 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
173 let Some(inner) = self.inner.as_mut() else {
174 return Poll::Ready(Ok(()));
175 };
176 match Pin::new(inner).poll_shutdown(cx) {
177 Poll::Ready(result) => {
178 self.note_progress();
179 Poll::Ready(result)
180 }
181 Poll::Pending => self.poll_stall(cx).map_ok(|_| ()),
182 }
183 }
184}
185
186#[cfg(test)]
187#[path = "write_timeout_tests.rs"]
188mod tests;