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 { inner, transport }) = &mut *self;
104
105 poll_peer_gone(
106 &inner.protocol_session,
107 inner.version,
108 &mut inner.buffer,
109 transport,
110 inner.peer_gone.as_mut(),
111 PROBE_WINDOW_CAP,
112 cx,
113 )
114 }
115}
116
117pub(crate) struct CancelOnDisconnect<'a, Fut, T>(
118 pub(crate) &'a mut Conn<T>,
119 pub(crate) Pin<&'a mut Fut>,
120);
121impl<'a, Fut, T> Future for CancelOnDisconnect<'a, Fut, T>
122where
123 Fut: Future + Send + 'a,
124 T: AsyncRead + AsyncWrite + Send + Sync + Unpin + 'static,
125{
126 type Output = Option<Fut::Output>;
127
128 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
129 let CancelOnDisconnect(conn, fut) = &mut *self;
130 let fut_poll = fut.as_mut().poll(cx);
131 let disconnect = Pin::new(&mut LivenessFut(conn)).poll(cx);
132 match (fut_poll, disconnect) {
133 (Poll::Ready(output), _) => Poll::Ready(Some(output)),
134 (Poll::Pending, Poll::Ready(())) => Poll::Ready(None),
135 (Poll::Pending, Poll::Pending) => Poll::Pending,
136 }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use crate::{HttpContext, ProtocolSession, h3::H3Connection};
144 use futures_lite::io::Cursor;
145 use std::sync::{
146 Arc,
147 atomic::{AtomicBool, Ordering},
148 };
149
150 fn gated(flag: Arc<AtomicBool>) -> PeerGone {
153 Box::pin(std::future::poll_fn(move |cx| {
154 if flag.load(Ordering::SeqCst) {
155 Poll::Ready(())
156 } else {
157 cx.waker().wake_by_ref();
158 Poll::Pending
159 }
160 }))
161 }
162
163 fn h3_session() -> ProtocolSession {
164 ProtocolSession::Http3 {
165 connection: H3Connection::new(Arc::new(HttpContext::new())),
166 stream_id: 0,
167 }
168 }
169
170 #[test]
174 fn h3_ignores_an_eof_transport() {
175 let mut transport = Cursor::new(Vec::new());
176 let mut buffer = Buffer::default();
177 let polled = futures_lite::future::block_on(std::future::poll_fn(|cx| {
178 Poll::Ready(poll_peer_gone(
179 &h3_session(),
180 Version::Http3,
181 &mut buffer,
182 &mut transport,
183 None,
184 16 * 1024,
185 cx,
186 ))
187 }));
188
189 assert!(
190 polled.is_pending(),
191 "an h3 client that finished sending its request has not departed"
192 );
193 }
194
195 #[test]
196 fn h3_reports_departure_only_once_signalled() {
197 let flag = Arc::new(AtomicBool::new(false));
198 let mut peer_gone = gated(flag.clone());
199 let mut transport = Cursor::new(Vec::new());
200 let mut buffer = Buffer::default();
201 let session = h3_session();
202
203 let poll =
204 |peer_gone: &mut PeerGone, transport: &mut Cursor<Vec<u8>>, buffer: &mut Buffer| {
205 futures_lite::future::block_on(std::future::poll_fn(|cx| {
206 Poll::Ready(poll_peer_gone(
207 &session,
208 Version::Http3,
209 buffer,
210 transport,
211 Some(peer_gone),
212 16 * 1024,
213 cx,
214 ))
215 }))
216 };
217
218 assert!(poll(&mut peer_gone, &mut transport, &mut buffer).is_pending());
219
220 flag.store(true, Ordering::SeqCst);
221 assert!(
222 poll(&mut peer_gone, &mut transport, &mut buffer).is_ready(),
223 "once the adapter signals abandonment, the probe must resolve"
224 );
225 }
226}