1use crate::{Buffer, Conn, ProtocolSession, Version};
2use futures_lite::{AsyncRead, AsyncWrite};
3use std::{
4 future::Future,
5 pin::Pin,
6 task::{Context, Poll},
7};
8
9const PROBE_WINDOW_CAP: usize = 16 * 1024;
12
13pub type PeerGone = Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
20
21pub(crate) fn poll_peer_gone<T: AsyncRead + Unpin>(
39 session: &ProtocolSession,
40 version: Version,
41 buffer: &mut Buffer,
42 transport: &mut T,
43 peer_gone: Option<&mut PeerGone>,
44 read_allowance: usize,
45 cx: &mut Context<'_>,
46) -> Poll<()> {
47 match session {
48 ProtocolSession::Http2 {
49 connection,
50 stream_id,
51 } => return connection.poll_stream_closed(*stream_id, cx),
52
53 ProtocolSession::Http3 { .. } => {
54 return match peer_gone {
55 Some(peer_gone) => peer_gone.as_mut().poll(cx),
56 None => Poll::Pending,
57 };
58 }
59
60 ProtocolSession::Http1 => {}
61 }
62
63 if !matches!(
66 version,
67 Version::Http0_9 | Version::Http1_0 | Version::Http1_1
68 ) {
69 return Poll::Pending;
70 }
71
72 loop {
73 let want = read_allowance.saturating_sub(buffer.live_len());
76 if want == 0 {
77 return Poll::Pending;
78 }
79
80 match Pin::new(&mut *transport).poll_read(cx, buffer.window(want)) {
81 Poll::Ready(Ok(0) | Err(_)) => return Poll::Ready(()),
82 Poll::Ready(Ok(n)) => buffer.advance(n),
83 Poll::Pending => return Poll::Pending,
84 }
85 }
86}
87
88pub(crate) struct LivenessFut<'a, T>(&'a mut Conn<T>);
89
90impl<'a, T> LivenessFut<'a, T> {
91 pub(crate) fn new(conn: &'a mut Conn<T>) -> Self {
92 Self(conn)
93 }
94}
95
96impl<T> Future for LivenessFut<'_, T>
97where
98 T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
99{
100 type Output = ();
101
102 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
103 let LivenessFut(Conn {
104 buffer,
105 transport,
106 protocol_session,
107 version,
108 peer_gone,
109 ..
110 }) = &mut *self;
111
112 poll_peer_gone(
113 protocol_session,
114 *version,
115 buffer,
116 transport,
117 peer_gone.as_mut(),
118 PROBE_WINDOW_CAP,
119 cx,
120 )
121 }
122}
123
124pub(crate) struct CancelOnDisconnect<'a, Fut, T>(
125 pub(crate) &'a mut Conn<T>,
126 pub(crate) Pin<&'a mut Fut>,
127);
128impl<'a, Fut, T> Future for CancelOnDisconnect<'a, Fut, T>
129where
130 Fut: Future + Send + 'a,
131 T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
132{
133 type Output = Option<Fut::Output>;
134
135 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
136 let CancelOnDisconnect(conn, fut) = &mut *self;
137 let fut_poll = fut.as_mut().poll(cx);
138 let disconnect = Pin::new(&mut LivenessFut(conn)).poll(cx);
139 match (fut_poll, disconnect) {
140 (Poll::Ready(output), _) => Poll::Ready(Some(output)),
141 (Poll::Pending, Poll::Ready(())) => Poll::Ready(None),
142 (Poll::Pending, Poll::Pending) => Poll::Pending,
143 }
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use crate::{HttpContext, ProtocolSession, h3::H3Connection};
151 use futures_lite::io::Cursor;
152 use std::sync::{
153 Arc,
154 atomic::{AtomicBool, Ordering},
155 };
156
157 fn gated(flag: Arc<AtomicBool>) -> PeerGone {
160 Box::pin(std::future::poll_fn(move |cx| {
161 if flag.load(Ordering::SeqCst) {
162 Poll::Ready(())
163 } else {
164 cx.waker().wake_by_ref();
165 Poll::Pending
166 }
167 }))
168 }
169
170 fn h3_session() -> ProtocolSession {
171 ProtocolSession::Http3 {
172 connection: H3Connection::new(Arc::new(HttpContext::new())),
173 stream_id: 0,
174 }
175 }
176
177 #[test]
181 fn h3_ignores_an_eof_transport() {
182 let mut transport = Cursor::new(Vec::new());
183 let mut buffer = Buffer::default();
184 let polled = futures_lite::future::block_on(std::future::poll_fn(|cx| {
185 Poll::Ready(poll_peer_gone(
186 &h3_session(),
187 Version::Http3,
188 &mut buffer,
189 &mut transport,
190 None,
191 16 * 1024,
192 cx,
193 ))
194 }));
195
196 assert!(
197 polled.is_pending(),
198 "an h3 client that finished sending its request has not departed"
199 );
200 }
201
202 #[test]
203 fn h3_reports_departure_only_once_signalled() {
204 let flag = Arc::new(AtomicBool::new(false));
205 let mut peer_gone = gated(flag.clone());
206 let mut transport = Cursor::new(Vec::new());
207 let mut buffer = Buffer::default();
208 let session = h3_session();
209
210 let poll =
211 |peer_gone: &mut PeerGone, transport: &mut Cursor<Vec<u8>>, buffer: &mut Buffer| {
212 futures_lite::future::block_on(std::future::poll_fn(|cx| {
213 Poll::Ready(poll_peer_gone(
214 &session,
215 Version::Http3,
216 buffer,
217 transport,
218 Some(peer_gone),
219 16 * 1024,
220 cx,
221 ))
222 }))
223 };
224
225 assert!(poll(&mut peer_gone, &mut transport, &mut buffer).is_pending());
226
227 flag.store(true, Ordering::SeqCst);
228 assert!(
229 poll(&mut peer_gone, &mut transport, &mut buffer).is_ready(),
230 "once the adapter signals abandonment, the probe must resolve"
231 );
232 }
233}