Skip to main content

hyper/body/
incoming.rs

1use std::fmt;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5use bytes::Bytes;
6#[cfg(all(
7    any(feature = "http1", feature = "http2"),
8    any(feature = "client", feature = "server")
9))]
10use futures_core::ready;
11use http_body::{Body, Frame, SizeHint};
12
13#[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
14use super::chan;
15#[cfg(all(
16    any(feature = "http1", feature = "http2"),
17    any(feature = "client", feature = "server")
18))]
19use super::DecodedLength;
20#[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
21use crate::proto::h2::ping;
22
23/// A stream of `Bytes`, used when receiving bodies from the network.
24///
25/// Note that Users should not instantiate this struct directly. When working with the hyper client,
26/// `Incoming` is returned to you in responses. Similarly, when operating with the hyper server,
27/// it is provided within requests.
28///
29/// # Examples
30///
31/// ```rust,ignore
32/// async fn echo(
33///    req: Request<hyper::body::Incoming>,
34/// ) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
35///    //Here, you can process `Incoming`
36/// }
37/// ```
38#[must_use = "streams do nothing unless polled"]
39pub struct Incoming {
40    kind: Kind,
41}
42
43enum Kind {
44    Empty,
45    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
46    Chan {
47        content_length: DecodedLength,
48        rx: chan::Receiver,
49    },
50    #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
51    H2 {
52        content_length: DecodedLength,
53        data_done: bool,
54        ping: ping::Recorder,
55        recv: h2::RecvStream,
56    },
57    #[cfg(feature = "ffi")]
58    Ffi(crate::ffi::UserBody),
59}
60
61/// A sender half created through [`Body::channel()`].
62///
63/// Useful when wanting to stream chunks from another thread.
64///
65/// ## Body Closing
66///
67/// Note that the request body will always be closed normally when the sender is dropped (meaning
68/// that the empty terminating chunk will be sent to the remote). If you desire to close the
69/// connection with an incomplete response (e.g. in the case of an error during asynchronous
70/// processing), call the [`Sender::abort()`] method to abort the body in an abnormal fashion.
71///
72/// [`Body::channel()`]: struct.Body.html#method.channel
73/// [`Sender::abort()`]: struct.Sender.html#method.abort
74#[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
75pub(crate) use super::chan::Sender;
76
77impl Incoming {
78    /// Create a `Body` stream with an associated sender half.
79    ///
80    /// Useful when wanting to stream chunks from another thread.
81    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
82    #[inline]
83    #[cfg(test)]
84    pub(crate) fn channel() -> (Sender, Incoming) {
85        Self::new_channel(DecodedLength::CHUNKED, /*wanter =*/ false)
86    }
87
88    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
89    pub(crate) fn new_channel(content_length: DecodedLength, wanter: bool) -> (Sender, Incoming) {
90        let (tx, rx) = chan::channel(wanter);
91        let rx = Incoming::new(Kind::Chan { content_length, rx });
92
93        (tx, rx)
94    }
95
96    fn new(kind: Kind) -> Incoming {
97        Incoming { kind }
98    }
99
100    #[allow(dead_code)]
101    pub(crate) fn empty() -> Incoming {
102        Incoming::new(Kind::Empty)
103    }
104
105    #[cfg(feature = "ffi")]
106    pub(crate) fn ffi() -> Incoming {
107        Incoming::new(Kind::Ffi(crate::ffi::UserBody::new()))
108    }
109
110    #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
111    pub(crate) fn h2(
112        recv: h2::RecvStream,
113        mut content_length: DecodedLength,
114        ping: ping::Recorder,
115    ) -> Self {
116        // If the stream is already EOS, then the "unknown length" is clearly
117        // actually ZERO.
118        if !content_length.is_exact() && recv.is_end_stream() {
119            content_length = DecodedLength::ZERO;
120        }
121
122        Incoming::new(Kind::H2 {
123            data_done: false,
124            ping,
125            content_length,
126            recv,
127        })
128    }
129
130    #[cfg(feature = "ffi")]
131    pub(crate) fn as_ffi_mut(&mut self) -> &mut crate::ffi::UserBody {
132        if !matches!(self.kind, Kind::Ffi(_)) {
133            self.kind = Kind::Ffi(crate::ffi::UserBody::new());
134        }
135
136        match &mut self.kind {
137            Kind::Ffi(body) => body,
138            _ => unreachable!(),
139        }
140    }
141}
142
143impl Body for Incoming {
144    type Data = Bytes;
145    type Error = crate::Error;
146
147    fn poll_frame(
148        #[cfg_attr(
149            not(all(
150                any(feature = "http1", feature = "http2"),
151                any(feature = "client", feature = "server")
152            )),
153            allow(unused_mut)
154        )]
155        mut self: Pin<&mut Self>,
156        #[cfg_attr(
157            not(all(
158                any(feature = "http1", feature = "http2"),
159                any(feature = "client", feature = "server")
160            )),
161            allow(unused_variables)
162        )]
163        cx: &mut Context<'_>,
164    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
165        match &mut self.kind {
166            Kind::Empty => Poll::Ready(None),
167            #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
168            Kind::Chan {
169                content_length: len,
170                rx,
171            } => {
172                if let Some(chunk) = ready!(rx.poll_next(cx)?) {
173                    len.sub_if(chunk.len() as u64);
174                    return Poll::Ready(Some(Ok(Frame::data(chunk))));
175                }
176                Poll::Ready(rx.take_trailers().map(Frame::trailers).map(Ok))
177            }
178            #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
179            Kind::H2 {
180                data_done,
181                ping,
182                recv: h2,
183                content_length: len,
184            } => {
185                if !*data_done {
186                    match ready!(h2.poll_data(cx)) {
187                        Some(Ok(bytes)) => {
188                            let _ = h2.flow_control().release_capacity(bytes.len());
189                            len.sub_if(bytes.len() as u64);
190                            ping.record_data(bytes.len());
191                            return Poll::Ready(Some(Ok(Frame::data(bytes))));
192                        }
193                        Some(Err(e)) => {
194                            if let Some(h2::Reason::NO_ERROR) = e.reason() {
195                                // As mentioned in RFC 7540 Section 8.1, a RST_STREAM with NO_ERROR
196                                // indicates an early response, and should cause the body reading
197                                // to stop, but not fail it:
198                                return Poll::Ready(None);
199                            } else {
200                                return Poll::Ready(Some(Err(crate::Error::new_body(e))));
201                            }
202                        }
203                        None => {
204                            *data_done = true;
205                            // fall through to trailers
206                        }
207                    }
208                }
209
210                // after data, check trailers
211                match ready!(h2.poll_trailers(cx)) {
212                    Ok(t) => {
213                        ping.record_non_data();
214                        Poll::Ready(Ok(t.map(Frame::trailers)).transpose())
215                    }
216                    Err(e) => {
217                        if let Some(h2::Reason::NO_ERROR) = e.reason() {
218                            // Same as above, a RST_STREAM with NO_ERROR indicates an early
219                            // response, and should cause reading the trailers to stop, but
220                            // not fail it:
221                            Poll::Ready(None)
222                        } else {
223                            Poll::Ready(Some(Err(crate::Error::new_h2(e))))
224                        }
225                    }
226                }
227            }
228
229            #[cfg(feature = "ffi")]
230            Kind::Ffi(body) => body.poll_data(cx),
231        }
232    }
233
234    fn is_end_stream(&self) -> bool {
235        match &self.kind {
236            Kind::Empty => true,
237            #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
238            Kind::Chan { content_length, .. } => *content_length == DecodedLength::ZERO,
239            #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
240            Kind::H2 { recv: h2, .. } => h2.is_end_stream(),
241            #[cfg(feature = "ffi")]
242            Kind::Ffi(..) => false,
243        }
244    }
245
246    fn size_hint(&self) -> SizeHint {
247        #[cfg(all(
248            any(feature = "http1", feature = "http2"),
249            any(feature = "client", feature = "server")
250        ))]
251        fn opt_len(decoded_length: DecodedLength) -> SizeHint {
252            if let Some(content_length) = decoded_length.into_opt() {
253                SizeHint::with_exact(content_length)
254            } else {
255                SizeHint::default()
256            }
257        }
258
259        match self.kind {
260            Kind::Empty => SizeHint::with_exact(0),
261            #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
262            Kind::Chan { content_length, .. } => opt_len(content_length),
263            #[cfg(all(feature = "http2", any(feature = "client", feature = "server")))]
264            Kind::H2 { content_length, .. } => opt_len(content_length),
265            #[cfg(feature = "ffi")]
266            Kind::Ffi(..) => SizeHint::default(),
267        }
268    }
269}
270
271impl fmt::Debug for Incoming {
272    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
273        #[cfg(any(
274            all(
275                any(feature = "http1", feature = "http2"),
276                any(feature = "client", feature = "server")
277            ),
278            feature = "ffi"
279        ))]
280        #[derive(Debug)]
281        struct Streaming;
282        #[derive(Debug)]
283        struct Empty;
284
285        let mut builder = f.debug_tuple("Body");
286        match self.kind {
287            Kind::Empty => builder.field(&Empty),
288            #[cfg(any(
289                all(
290                    any(feature = "http1", feature = "http2"),
291                    any(feature = "client", feature = "server")
292                ),
293                feature = "ffi"
294            ))]
295            _ => builder.field(&Streaming),
296        };
297
298        builder.finish()
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
305    use std::mem;
306    #[cfg(all(feature = "nightly", not(miri)))]
307    use std::pin::Pin;
308    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
309    use std::task::Poll;
310
311    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
312    use super::{Body, Incoming, SizeHint};
313    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
314    use super::{DecodedLength, Sender};
315    #[cfg(all(feature = "nightly", not(miri)))]
316    use bytes::Bytes;
317    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
318    use http_body_util::BodyExt;
319
320    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
321    #[test]
322    fn test_size_of() {
323        // These are mostly to help catch *accidentally* increasing
324        // the size by too much.
325
326        let body_size = mem::size_of::<Incoming>();
327        let body_expected_size = mem::size_of::<u64>() * 5;
328        assert!(
329            body_size <= body_expected_size,
330            "Body size = {} <= {}",
331            body_size,
332            body_expected_size,
333        );
334
335        assert_eq!(
336            mem::size_of::<Sender>(),
337            mem::size_of::<usize>() * 2,
338            "Sender"
339        );
340
341        assert_eq!(
342            mem::size_of::<Sender>(),
343            mem::size_of::<Option<Sender>>(),
344            "Option<Sender>"
345        );
346    }
347
348    #[cfg(all(feature = "nightly", not(miri)))]
349    #[bench]
350    fn bench_channel_create_and_drop(b: &mut test::Bencher) {
351        b.iter(|| {
352            let _ = test::black_box(Incoming::new_channel(
353                DecodedLength::CHUNKED,
354                /* wanter = */ false,
355            ));
356        });
357    }
358
359    #[cfg(all(feature = "nightly", not(miri)))]
360    #[bench]
361    fn bench_channel_data_handoff(b: &mut test::Bencher) {
362        let (mut tx, mut body) =
363            Incoming::new_channel(DecodedLength::CHUNKED, /* wanter = */ false);
364        let mut cx = std::task::Context::from_waker(std::task::Waker::noop());
365
366        b.iter(|| {
367            assert!(tx.poll_ready(&mut cx).is_ready());
368            tx.try_send_data(Bytes::from_static(b"hello world"))
369                .unwrap();
370            let frame = match Pin::new(&mut body).poll_frame(&mut cx) {
371                Poll::Ready(Some(Ok(frame))) => frame,
372                unexpected => panic!("unexpected body poll: {unexpected:?}"),
373            };
374            test::black_box(frame);
375        });
376    }
377
378    #[cfg(all(feature = "nightly", not(miri)))]
379    #[bench]
380    fn bench_channel_want_transition(b: &mut test::Bencher) {
381        let mut cx = std::task::Context::from_waker(std::task::Waker::noop());
382
383        b.iter(|| {
384            let (mut tx, mut body) =
385                Incoming::new_channel(DecodedLength::CHUNKED, /* wanter = */ true);
386            assert!(tx.poll_ready(&mut cx).is_pending());
387            assert!(Pin::new(&mut body).poll_frame(&mut cx).is_pending());
388            assert!(tx.poll_ready(&mut cx).is_ready());
389            let _ = test::black_box((tx, body));
390        });
391    }
392
393    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
394    #[test]
395    fn size_hint() {
396        fn eq(body: Incoming, b: SizeHint, note: &str) {
397            let a = body.size_hint();
398            assert_eq!(a.lower(), b.lower(), "lower for {:?}", note);
399            assert_eq!(a.upper(), b.upper(), "upper for {:?}", note);
400        }
401
402        eq(Incoming::empty(), SizeHint::with_exact(0), "empty");
403
404        eq(Incoming::channel().1, SizeHint::new(), "channel");
405
406        eq(
407            Incoming::new_channel(DecodedLength::new(4), /*wanter =*/ false).1,
408            SizeHint::with_exact(4),
409            "channel with length",
410        );
411    }
412
413    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
414    #[cfg(not(miri))]
415    #[tokio::test]
416    async fn channel_abort() {
417        let (tx, mut rx) = Incoming::channel();
418
419        tx.abort();
420
421        let err = rx.frame().await.unwrap().unwrap_err();
422        assert!(err.is_body_write_aborted(), "{:?}", err);
423    }
424
425    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
426    #[cfg(all(not(miri), feature = "http1"))]
427    #[tokio::test]
428    async fn channel_abort_when_buffer_is_full() {
429        let (mut tx, mut rx) = Incoming::channel();
430
431        tx.try_send_data("chunk 1".into()).expect("send 1");
432        // buffer is full, but can still send abort
433        tx.abort();
434
435        let chunk1 = rx
436            .frame()
437            .await
438            .expect("item 1")
439            .expect("chunk 1")
440            .into_data()
441            .unwrap();
442        assert_eq!(chunk1, "chunk 1");
443
444        let err = rx.frame().await.unwrap().unwrap_err();
445        assert!(err.is_body_write_aborted(), "{:?}", err);
446    }
447
448    #[cfg(feature = "http1")]
449    #[test]
450    fn channel_buffers_one() {
451        let (mut tx, _rx) = Incoming::channel();
452
453        tx.try_send_data("chunk 1".into()).expect("send 1");
454
455        // buffer is now full
456        let chunk2 = tx.try_send_data("chunk 2".into()).expect_err("send 2");
457        assert_eq!(chunk2, "chunk 2");
458    }
459
460    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
461    #[cfg(not(miri))]
462    #[tokio::test]
463    async fn channel_empty() {
464        let (_, mut rx) = Incoming::channel();
465
466        assert!(rx.frame().await.is_none());
467    }
468
469    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
470    #[test]
471    fn channel_ready() {
472        let (mut tx, _rx) = Incoming::new_channel(DecodedLength::CHUNKED, /*wanter = */ false);
473
474        let mut tx_ready = tokio_test::task::spawn(tx.ready());
475
476        assert!(tx_ready.poll().is_ready(), "tx is ready immediately");
477    }
478
479    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
480    #[test]
481    fn channel_wanter() {
482        let (mut tx, mut rx) =
483            Incoming::new_channel(DecodedLength::CHUNKED, /*wanter = */ true);
484
485        let mut tx_ready = tokio_test::task::spawn(tx.ready());
486        let mut rx_data = tokio_test::task::spawn(rx.frame());
487
488        assert!(
489            tx_ready.poll().is_pending(),
490            "tx isn't ready before rx has been polled"
491        );
492
493        assert!(rx_data.poll().is_pending(), "poll rx.data");
494        assert!(tx_ready.is_woken(), "rx poll wakes tx");
495
496        assert!(
497            tx_ready.poll().is_ready(),
498            "tx is ready after rx has been polled"
499        );
500    }
501
502    #[cfg(all(feature = "http1", any(feature = "client", feature = "server")))]
503    #[test]
504    fn channel_notices_closure() {
505        let (mut tx, rx) = Incoming::new_channel(DecodedLength::CHUNKED, /*wanter = */ true);
506
507        let mut tx_ready = tokio_test::task::spawn(tx.ready());
508
509        assert!(
510            tx_ready.poll().is_pending(),
511            "tx isn't ready before rx has been polled"
512        );
513
514        drop(rx);
515        assert!(tx_ready.is_woken(), "dropping rx wakes tx");
516
517        match &tx_ready.poll() {
518            Poll::Ready(Err(e)) if e.is_closed() => (),
519            unexpected => panic!("tx poll ready unexpected: {:?}", unexpected),
520        }
521    }
522}