Skip to main content

hyper/proto/h2/
upgrade.rs

1use std::future::Future;
2use std::io::Cursor;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use bytes::{Buf, Bytes};
7use futures_channel::{mpsc, oneshot};
8use futures_core::{ready, Stream};
9use h2::{Reason, RecvStream, SendStream};
10use pin_project_lite::pin_project;
11
12use super::ping::Recorder;
13use super::SendBuf;
14use crate::rt::{Read, ReadBufCursor, Write};
15
16pub(super) fn pair<B>(
17    send_stream: SendStream<SendBuf<B>>,
18    recv_stream: RecvStream,
19    ping: Recorder,
20) -> (H2Upgraded, UpgradedSendStreamTask<B>) {
21    let (tx, rx) = mpsc::channel(1);
22    let (error_tx, error_rx) = oneshot::channel();
23
24    (
25        H2Upgraded {
26            send_stream: UpgradedSendStreamBridge { tx, error_rx },
27            recv_stream,
28            ping,
29            buf: Bytes::new(),
30        },
31        UpgradedSendStreamTask {
32            h2_tx: send_stream,
33            rx,
34            buffered: None,
35            error_tx: Some(error_tx),
36        },
37    )
38}
39
40pub(super) struct H2Upgraded {
41    ping: Recorder,
42    send_stream: UpgradedSendStreamBridge,
43    recv_stream: RecvStream,
44    buf: Bytes,
45}
46
47struct UpgradedSendStreamBridge {
48    tx: mpsc::Sender<Cursor<Box<[u8]>>>,
49    error_rx: oneshot::Receiver<crate::Error>,
50}
51
52pin_project! {
53    #[must_use = "futures do nothing unless polled"]
54    pub struct UpgradedSendStreamTask<B> {
55        #[pin]
56        h2_tx: SendStream<SendBuf<B>>,
57        #[pin]
58        rx: mpsc::Receiver<Cursor<Box<[u8]>>>,
59        buffered: Option<Cursor<Box<[u8]>>>,
60        error_tx: Option<oneshot::Sender<crate::Error>>,
61    }
62}
63
64// ===== impl UpgradedSendStreamTask =====
65
66impl<B> UpgradedSendStreamTask<B>
67where
68    B: Buf,
69{
70    fn tick(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), crate::Error>> {
71        let mut me = self.project();
72
73        // this is a manual `select()` over 3 "futures", so we always need
74        // to be sure they are ready and/or we are waiting notification of
75        // one of the sides hanging up, so the task doesn't live around
76        // longer than it's meant to.
77        loop {
78            match me.h2_tx.poll_reset(cx) {
79                Poll::Ready(Ok(reason)) => {
80                    trace!("stream received RST_STREAM: {:?}", reason);
81                    return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
82                        reason,
83                    ))));
84                }
85                Poll::Ready(Err(err)) => {
86                    return Poll::Ready(Err(crate::Error::new_body_write(err)))
87                }
88                Poll::Pending => (),
89            }
90
91            // A write taken from the mpsc receiver waits here for h2 capacity,
92            // and the next one isn't pulled until it has been handed to h2, so
93            // the writer still sees h2 backpressure.
94            if me.buffered.is_some() {
95                // poll_capacity oddly needs a loop
96                while me.h2_tx.capacity() == 0 {
97                    match ready!(me.h2_tx.poll_capacity(cx)) {
98                        Some(Ok(0)) => {}
99                        Some(Ok(_)) => break,
100                        Some(Err(e)) => return Poll::Ready(Err(crate::Error::new_body_write(e))),
101                        None => {
102                            // None means the stream is no longer in a
103                            // streaming state, we either finished it
104                            // somehow, or the remote reset us.
105                            return Poll::Ready(Err(crate::Error::new_body_write(
106                                "send stream capacity unexpectedly closed",
107                            )));
108                        }
109                    }
110                }
111
112                let cursor = me.buffered.take().expect("checked is_some above");
113                me.h2_tx
114                    .send_data(SendBuf::Cursor(cursor), false)
115                    .map_err(crate::Error::new_body_write)?;
116                continue;
117            }
118
119            match me.rx.as_mut().poll_next(cx) {
120                Poll::Ready(Some(cursor)) => {
121                    // Only reserve capacity once there is something to send.
122                    // Reserving while idle, even a single byte, pins that
123                    // capacity on the connection-level window (#4003). As in
124                    // `PipeToSendStream`, h2 raises the request to the
125                    // buffered length inside `send_data`.
126                    me.h2_tx.reserve_capacity(1);
127                    *me.buffered = Some(cursor);
128                }
129                Poll::Ready(None) => {
130                    me.h2_tx
131                        .send_data(SendBuf::None, true)
132                        .map_err(crate::Error::new_body_write)?;
133                    return Poll::Ready(Ok(()));
134                }
135                Poll::Pending => {
136                    return Poll::Pending;
137                }
138            }
139        }
140    }
141}
142
143impl<B> Future for UpgradedSendStreamTask<B>
144where
145    B: Buf,
146{
147    type Output = ();
148
149    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
150        match self.as_mut().tick(cx) {
151            Poll::Ready(Ok(())) => Poll::Ready(()),
152            Poll::Ready(Err(err)) => {
153                if let Some(tx) = self.error_tx.take() {
154                    let _oh_well = tx.send(err);
155                }
156                Poll::Ready(())
157            }
158            Poll::Pending => Poll::Pending,
159        }
160    }
161}
162
163// ===== impl H2Upgraded =====
164
165impl Read for H2Upgraded {
166    fn poll_read(
167        mut self: Pin<&mut Self>,
168        cx: &mut Context<'_>,
169        mut read_buf: ReadBufCursor<'_>,
170    ) -> Poll<Result<(), std::io::Error>> {
171        if self.buf.is_empty() {
172            self.buf = loop {
173                match ready!(self.recv_stream.poll_data(cx)) {
174                    None => return Poll::Ready(Ok(())),
175                    Some(Ok(buf)) if buf.is_empty() && !self.recv_stream.is_end_stream() => {}
176                    Some(Ok(buf)) => {
177                        self.ping.record_data(buf.len());
178                        break buf;
179                    }
180                    Some(Err(e)) => {
181                        return Poll::Ready(match e.reason() {
182                            Some(Reason::NO_ERROR) | Some(Reason::CANCEL) => Ok(()),
183                            Some(Reason::STREAM_CLOSED) => {
184                                Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, e))
185                            }
186                            _ => Err(h2_to_io_error(e)),
187                        })
188                    }
189                }
190            };
191        }
192        let cnt = std::cmp::min(self.buf.len(), read_buf.remaining());
193        read_buf.put_slice(&self.buf[..cnt]);
194        self.buf.advance(cnt);
195        let _ = self.recv_stream.flow_control().release_capacity(cnt);
196        Poll::Ready(Ok(()))
197    }
198}
199
200impl Write for H2Upgraded {
201    fn poll_write(
202        mut self: Pin<&mut Self>,
203        cx: &mut Context<'_>,
204        buf: &[u8],
205    ) -> Poll<Result<usize, std::io::Error>> {
206        if buf.is_empty() {
207            return Poll::Ready(Ok(0));
208        }
209
210        match self.send_stream.tx.poll_ready(cx) {
211            Poll::Ready(Ok(())) => {}
212            Poll::Ready(Err(_task_dropped)) => {
213                // if the task dropped, check if there was an error
214                // otherwise i guess its a broken pipe
215                return match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
216                    Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
217                    Poll::Ready(Err(_task_dropped)) => {
218                        Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into()))
219                    }
220                    Poll::Pending => Poll::Pending,
221                };
222            }
223            Poll::Pending => return Poll::Pending,
224        }
225
226        let n = buf.len();
227        match self.send_stream.tx.start_send(Cursor::new(buf.into())) {
228            Ok(()) => Poll::Ready(Ok(n)),
229            Err(_task_dropped) => {
230                // if the task dropped, check if there was an error
231                // otherwise i guess its a broken pipe
232                match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
233                    Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
234                    Poll::Ready(Err(_task_dropped)) => {
235                        Poll::Ready(Err(std::io::ErrorKind::BrokenPipe.into()))
236                    }
237                    Poll::Pending => Poll::Pending,
238                }
239            }
240        }
241    }
242
243    fn poll_flush(
244        mut self: Pin<&mut Self>,
245        cx: &mut Context<'_>,
246    ) -> Poll<Result<(), std::io::Error>> {
247        match self.send_stream.tx.poll_ready(cx) {
248            Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
249            Poll::Ready(Err(_task_dropped)) => {
250                // if the task dropped, check if there was an error
251                // otherwise it was a clean close
252                match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
253                    Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
254                    Poll::Ready(Err(_task_dropped)) => Poll::Ready(Ok(())),
255                    Poll::Pending => Poll::Pending,
256                }
257            }
258            Poll::Pending => Poll::Pending,
259        }
260    }
261
262    fn poll_shutdown(
263        mut self: Pin<&mut Self>,
264        cx: &mut Context<'_>,
265    ) -> Poll<Result<(), std::io::Error>> {
266        self.send_stream.tx.close_channel();
267        match Pin::new(&mut self.send_stream.error_rx).poll(cx) {
268            Poll::Ready(Ok(reason)) => Poll::Ready(Err(io_error(reason))),
269            Poll::Ready(Err(_task_dropped)) => Poll::Ready(Ok(())),
270            Poll::Pending => Poll::Pending,
271        }
272    }
273}
274
275fn io_error(e: crate::Error) -> std::io::Error {
276    std::io::Error::new(std::io::ErrorKind::Other, e)
277}
278
279fn h2_to_io_error(e: h2::Error) -> std::io::Error {
280    if e.is_io() {
281        e.into_io()
282            .expect("h2 error reported io cause without an underlying io error")
283    } else {
284        std::io::Error::new(std::io::ErrorKind::Other, e)
285    }
286}