Skip to main content

hyper/proto/h2/
upgrade.rs

1use std::future::Future;
2use std::io::Cursor;
3use std::pin::Pin;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::Arc;
6use std::task::{Context, Poll};
7
8use atomic_waker::AtomicWaker;
9use bytes::{Buf, Bytes};
10use futures_channel::{mpsc, oneshot};
11use futures_core::{ready, Stream};
12use h2::{Reason, RecvStream, SendStream};
13use pin_project_lite::pin_project;
14
15use super::ping::Recorder;
16use super::SendBuf;
17use crate::rt::{Read, ReadBufCursor, Write};
18
19pub(super) fn pair<B>(
20    send_stream: SendStream<SendBuf<B>>,
21    recv_stream: RecvStream,
22    ping: Recorder,
23) -> (H2Upgraded, UpgradedSendStreamTask<B>) {
24    let (tx, rx) = mpsc::channel(1);
25    let (error_tx, error_rx) = oneshot::channel();
26    let close_notify = Arc::new(UpgradedCloseNotify::new());
27
28    (
29        H2Upgraded {
30            send_stream: UpgradedSendStreamBridge {
31                tx,
32                error_rx,
33                close_notify: close_notify.clone(),
34            },
35            recv_stream,
36            ping,
37            buf: Bytes::new(),
38        },
39        UpgradedSendStreamTask {
40            h2_tx: send_stream,
41            rx,
42            close_notify,
43            error_tx: Some(error_tx),
44        },
45    )
46}
47
48pub(super) struct H2Upgraded {
49    ping: Recorder,
50    send_stream: UpgradedSendStreamBridge,
51    recv_stream: RecvStream,
52    buf: Bytes,
53}
54
55struct UpgradedSendStreamBridge {
56    tx: mpsc::Sender<Cursor<Box<[u8]>>>,
57    error_rx: oneshot::Receiver<crate::Error>,
58    close_notify: Arc<UpgradedCloseNotify>,
59}
60
61impl Drop for UpgradedSendStreamBridge {
62    fn drop(&mut self) {
63        self.close_notify.close();
64    }
65}
66
67struct UpgradedCloseNotify {
68    closed: AtomicBool,
69    task: AtomicWaker,
70}
71
72impl UpgradedCloseNotify {
73    fn new() -> Self {
74        Self {
75            closed: AtomicBool::new(false),
76            task: AtomicWaker::new(),
77        }
78    }
79
80    fn close(&self) {
81        self.closed.store(true, Ordering::Release);
82        self.task.wake();
83    }
84
85    fn poll_closed(&self, cx: &mut Context<'_>) -> Poll<()> {
86        if self.closed.load(Ordering::Acquire) {
87            return Poll::Ready(());
88        }
89
90        self.task.register(cx.waker());
91
92        if self.closed.load(Ordering::Acquire) {
93            Poll::Ready(())
94        } else {
95            Poll::Pending
96        }
97    }
98}
99
100pin_project! {
101    #[must_use = "futures do nothing unless polled"]
102    pub struct UpgradedSendStreamTask<B> {
103        #[pin]
104        h2_tx: SendStream<SendBuf<B>>,
105        #[pin]
106        rx: mpsc::Receiver<Cursor<Box<[u8]>>>,
107        close_notify: Arc<UpgradedCloseNotify>,
108        error_tx: Option<oneshot::Sender<crate::Error>>,
109    }
110}
111
112// ===== impl UpgradedSendStreamTask =====
113
114impl<B> UpgradedSendStreamTask<B>
115where
116    B: Buf,
117{
118    fn tick(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), crate::Error>> {
119        let mut me = self.project();
120
121        // this is a manual `select()` over 3 "futures", so we always need
122        // to be sure they are ready and/or we are waiting notification of
123        // one of the sides hanging up, so the task doesn't live around
124        // longer than it's meant to.
125        loop {
126            // we don't have the next chunk of data yet, so just reserve 1 byte to make
127            // sure there's some capacity available. h2 will handle the capacity management
128            // for the actual body chunk.
129            me.h2_tx.reserve_capacity(1);
130
131            let h2_has_capacity = if me.h2_tx.capacity() == 0 {
132                // poll_capacity oddly needs a loop
133                loop {
134                    match me.h2_tx.poll_capacity(cx) {
135                        Poll::Ready(Some(Ok(0))) => {}
136                        Poll::Ready(Some(Ok(_))) => break true,
137                        Poll::Ready(Some(Err(e))) => {
138                            return Poll::Ready(Err(crate::Error::new_body_write(e)))
139                        }
140                        Poll::Ready(None) => {
141                            // None means the stream is no longer in a
142                            // streaming state, we either finished it
143                            // somehow, or the remote reset us.
144                            return Poll::Ready(Err(crate::Error::new_body_write(
145                                "send stream capacity unexpectedly closed",
146                            )));
147                        }
148                        Poll::Pending => break false,
149                    }
150                }
151            } else {
152                true
153            };
154
155            match me.h2_tx.poll_reset(cx) {
156                Poll::Ready(Ok(reason)) => {
157                    trace!("stream received RST_STREAM: {:?}", reason);
158                    return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
159                        reason,
160                    ))));
161                }
162                Poll::Ready(Err(err)) => {
163                    return Poll::Ready(Err(crate::Error::new_body_write(err)))
164                }
165                Poll::Pending => (),
166            }
167
168            // If h2 has no capacity, don't pull another item from the mpsc
169            // receiver. That would free a channel slot and let the writer
170            // enqueue more data without h2 backpressure.
171            //
172            // Still allow the task to finish once the upgraded write side is
173            // gone and the mpsc queue is empty.
174            if !h2_has_capacity {
175                // `size_hint` reads the queued message count without popping,
176                // so an accepted write stays queued until h2 capacity returns.
177                if me.rx.size_hint().0 == 0 && me.close_notify.poll_closed(cx).is_ready() {
178                    me.h2_tx
179                        .send_data(SendBuf::None, true)
180                        .map_err(crate::Error::new_body_write)?;
181                    return Poll::Ready(Ok(()));
182                }
183
184                return Poll::Pending;
185            }
186
187            match me.rx.as_mut().poll_next(cx) {
188                Poll::Ready(Some(cursor)) => {
189                    me.h2_tx
190                        .send_data(SendBuf::Cursor(cursor), false)
191                        .map_err(crate::Error::new_body_write)?;
192                }
193                Poll::Ready(None) => {
194                    me.h2_tx
195                        .send_data(SendBuf::None, true)
196                        .map_err(crate::Error::new_body_write)?;
197                    return Poll::Ready(Ok(()));
198                }
199                Poll::Pending => {
200                    return Poll::Pending;
201                }
202            }
203        }
204    }
205}
206
207impl<B> Future for UpgradedSendStreamTask<B>
208where
209    B: Buf,
210{
211    type Output = ();
212
213    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
214        match self.as_mut().tick(cx) {
215            Poll::Ready(Ok(())) => Poll::Ready(()),
216            Poll::Ready(Err(err)) => {
217                if let Some(tx) = self.error_tx.take() {
218                    let _oh_well = tx.send(err);
219                }
220                Poll::Ready(())
221            }
222            Poll::Pending => Poll::Pending,
223        }
224    }
225}
226
227// ===== impl H2Upgraded =====
228
229impl Read for H2Upgraded {
230    fn poll_read(
231        mut self: Pin<&mut Self>,
232        cx: &mut Context<'_>,
233        mut read_buf: ReadBufCursor<'_>,
234    ) -> Poll<Result<(), std::io::Error>> {
235        if self.buf.is_empty() {
236            self.buf = loop {
237                match ready!(self.recv_stream.poll_data(cx)) {
238                    None => return Poll::Ready(Ok(())),
239                    Some(Ok(buf)) if buf.is_empty() && !self.recv_stream.is_end_stream() => {
240                        continue
241                    }
242                    Some(Ok(buf)) => {
243                        self.ping.record_data(buf.len());
244                        break buf;
245                    }
246                    Some(Err(e)) => {
247                        return Poll::Ready(match e.reason() {
248                            Some(Reason::NO_ERROR) | Some(Reason::CANCEL) => Ok(()),
249                            Some(Reason::STREAM_CLOSED) => {
250                                Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, e))
251                            }
252                            _ => Err(h2_to_io_error(e)),
253                        })
254                    }
255                }
256            };
257        }
258        let cnt = std::cmp::min(self.buf.len(), read_buf.remaining());
259        read_buf.put_slice(&self.buf[..cnt]);
260        self.buf.advance(cnt);
261        let _ = self.recv_stream.flow_control().release_capacity(cnt);
262        Poll::Ready(Ok(()))
263    }
264}
265
266impl Write for H2Upgraded {
267    fn poll_write(
268        mut self: Pin<&mut Self>,
269        cx: &mut Context<'_>,
270        buf: &[u8],
271    ) -> Poll<Result<usize, std::io::Error>> {
272        if buf.is_empty() {
273            return Poll::Ready(Ok(0));
274        }
275
276        match self.send_stream.tx.poll_ready(cx) {
277            Poll::Ready(Ok(())) => {}
278            Poll::Ready(Err(_task_dropped)) => {
279                // if the task dropped, check if there was an error
280                // otherwise i guess its a broken pipe
281                return match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
282                    Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
283                    Poll::Ready(Err(_task_dropped)) => {
284                        Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into()))
285                    }
286                    Poll::Pending => Poll::Pending,
287                };
288            }
289            Poll::Pending => return Poll::Pending,
290        }
291
292        let n = buf.len();
293        match self.send_stream.tx.start_send(Cursor::new(buf.into())) {
294            Ok(()) => Poll::Ready(Ok(n)),
295            Err(_task_dropped) => {
296                // if the task dropped, check if there was an error
297                // otherwise i guess its a broken pipe
298                match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
299                    Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
300                    Poll::Ready(Err(_task_dropped)) => {
301                        Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into()))
302                    }
303                    Poll::Pending => Poll::Pending,
304                }
305            }
306        }
307    }
308
309    fn poll_flush(
310        mut self: Pin<&mut Self>,
311        cx: &mut Context<'_>,
312    ) -> Poll<Result<(), std::io::Error>> {
313        match self.send_stream.tx.poll_ready(cx) {
314            Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
315            Poll::Ready(Err(_task_dropped)) => {
316                // if the task dropped, check if there was an error
317                // otherwise it was a clean close
318                match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
319                    Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
320                    Poll::Ready(Err(_task_dropped)) => Poll::Ready(Ok(())),
321                    Poll::Pending => Poll::Pending,
322                }
323            }
324            Poll::Pending => Poll::Pending,
325        }
326    }
327
328    fn poll_shutdown(
329        mut self: Pin<&mut Self>,
330        cx: &mut Context<'_>,
331    ) -> Poll<Result<(), std::io::Error>> {
332        self.send_stream.tx.close_channel();
333        self.send_stream.close_notify.close();
334        match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
335            Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
336            Poll::Ready(Err(_task_dropped)) => Poll::Ready(Ok(())),
337            Poll::Pending => Poll::Pending,
338        }
339    }
340}
341
342fn io_error(e: crate::Error) -> std::io::Error {
343    std::io::Error::new(std::io::ErrorKind::Other, e)
344}
345
346fn h2_to_io_error(e: h2::Error) -> std::io::Error {
347    if e.is_io() {
348        e.into_io()
349            .expect("h2 error reported io cause without an underlying io error")
350    } else {
351        std::io::Error::new(std::io::ErrorKind::Other, e)
352    }
353}