Skip to main content

simple_hyper_client/body/
mod.rs

1/* Copyright (c) Fortanix, Inc.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7use self::shared::{SharedBody, SharedBuf};
8
9use http_body_util::combinators::BoxBody;
10use http_body_util::BodyExt;
11use hyper::body::{Body, Buf, Bytes, Frame, SizeHint};
12
13use std::error::Error;
14use std::pin::Pin;
15use std::task::{Context, Poll};
16
17pub(crate) mod shared;
18
19/// This is an implementation of `hyper::body::Body` for use with HTTP
20/// `Request`s.
21///
22/// This can be constructed from `Arc<Vec<u8>>`, allowing the data to be
23/// shared. The type can also wrap arbitrary other `hyper::body::Body`
24/// instances, as long as they have `Bytes` as their `Data` associated type.
25pub struct RequestBody(InnerBody);
26
27enum InnerBody {
28    Shared(SharedBody),
29    Wrapped(BoxBody<Bytes, Box<dyn Error + Send + Sync>>),
30}
31
32impl RequestBody {
33    /// Create an empty request body.
34    pub fn empty() -> Self {
35        SharedBody::empty().into()
36    }
37
38    /// Create a `RequestBody` from an arbitrary other `hyper::body::Body`.
39    pub fn wrap<B, E>(body: B) -> Self
40    where
41        B: Body<Data = Bytes, Error = E> + Send + Sync + 'static,
42        E: Error + Send + Sync + 'static,
43    {
44        // Note: we do not support wrapping bodies with arbitrary `Buf`s as their
45        // `Data` associated type, because that would require us to call
46        // `BodyExt::map_frame()`, which returns a body type that does not provide
47        // accurate size hints, which can affect the transfer-encoding and
48        // content-length headers sent along with the request.
49        Self(InnerBody::Wrapped(
50            body.map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
51                .boxed(),
52        ))
53    }
54}
55
56impl Default for RequestBody {
57    /// Returns `Self::empty()`.
58    #[inline]
59    fn default() -> Self {
60        Self::empty()
61    }
62}
63
64impl<T: Into<SharedBody>> From<T> for RequestBody {
65    fn from(shared_body: T) -> Self {
66        Self(InnerBody::Shared(shared_body.into()))
67    }
68}
69
70impl Body for RequestBody {
71    type Data = Buffer;
72    type Error = Box<dyn Error + Send + Sync>;
73
74    fn poll_frame(
75        self: Pin<&mut Self>,
76        cx: &mut Context<'_>,
77    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
78        match &mut self.get_mut().0 {
79            InnerBody::Shared(shared_body) => SharedBody::poll_frame(Pin::new(shared_body), cx)
80                .map(|opt| {
81                    opt.map(|res| {
82                        res.map(|frame| frame.map_data(|bytes| Buffer::Shared(bytes)))
83                            .map_err(|e| Box::new(e) as Box<dyn Error + Send + Sync>)
84                    })
85                }),
86
87            InnerBody::Wrapped(box_body) => {
88                BoxBody::poll_frame(Pin::new(box_body), cx).map(|opt| {
89                    opt.map(|res| res.map(|frame| frame.map_data(|bytes| Buffer::Wrapped(bytes))))
90                })
91            }
92        }
93    }
94
95    fn is_end_stream(&self) -> bool {
96        match &self.0 {
97            InnerBody::Shared(shared_body) => shared_body.is_end_stream(),
98            InnerBody::Wrapped(box_body) => box_body.is_end_stream(),
99        }
100    }
101
102    fn size_hint(&self) -> SizeHint {
103        match &self.0 {
104            InnerBody::Shared(shared_body) => shared_body.size_hint(),
105            InnerBody::Wrapped(box_body) => box_body.size_hint(),
106        }
107    }
108}
109
110/// The `hyper::body::Body::Data` type for a [`RequestBody`].
111pub enum Buffer {
112    Shared(SharedBuf),
113    Wrapped(Bytes),
114}
115
116impl Buf for Buffer {
117    fn remaining(&self) -> usize {
118        match self {
119            Self::Shared(shared_buf) => shared_buf.remaining(),
120            Self::Wrapped(bytes) => bytes.remaining(),
121        }
122    }
123
124    fn chunk(&self) -> &[u8] {
125        match self {
126            Self::Shared(shared_buf) => shared_buf.chunk(),
127            Self::Wrapped(bytes) => bytes.chunk(),
128        }
129    }
130
131    fn advance(&mut self, cnt: usize) {
132        match self {
133            Self::Shared(shared_buf) => shared_buf.advance(cnt),
134            Self::Wrapped(bytes) => bytes.advance(cnt),
135        }
136    }
137}