tower_hyper_http_body_compat/
body.rs

1use std::{
2    pin::Pin,
3    task::{Context, Poll},
4};
5
6use http::HeaderMap;
7use http_body_1::Frame;
8use pin_project_lite::pin_project;
9
10// --- http-body 0.4 to http-body 1.0 ---
11
12pin_project! {
13    /// Converts an [http-body 0.4 `Body`] to an [http-body 1.0 `Body`].
14    ///
15    /// [http-body 0.4 `Body`]: https://docs.rs/http-body/latest/http_body/trait.Body.html
16    /// [http-body 1.0 `Body`]: https://docs.rs/http-body/1.0.0-rc.2/http_body/trait.Body.html
17    #[derive(Debug, Clone, Copy)]
18    pub struct HttpBody04ToHttpBody1<B> {
19        #[pin]
20        body: B,
21    }
22}
23
24impl<B> HttpBody04ToHttpBody1<B> {
25    /// Create a new `HttpBody04ToHttpBody1`.
26    #[inline]
27    pub fn new(body: B) -> Self {
28        Self { body }
29    }
30}
31
32impl<B> http_body_1::Body for HttpBody04ToHttpBody1<B>
33where
34    B: http_body_04::Body,
35{
36    type Data = B::Data;
37    type Error = B::Error;
38
39    fn poll_frame(
40        mut self: Pin<&mut Self>,
41        cx: &mut Context<'_>,
42    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
43        match self.as_mut().project().body.poll_data(cx) {
44            Poll::Ready(Some(Ok(buf))) => return Poll::Ready(Some(Ok(Frame::data(buf)))),
45            Poll::Ready(Some(Err(err))) => return Poll::Ready(Some(Err(err))),
46            Poll::Ready(None) => {}
47            Poll::Pending => return Poll::Pending,
48        }
49
50        match self.as_mut().project().body.poll_trailers(cx) {
51            Poll::Ready(Ok(Some(trailers))) => Poll::Ready(Some(Ok(Frame::trailers(trailers)))),
52            Poll::Ready(Ok(None)) => Poll::Ready(None),
53            Poll::Ready(Err(err)) => Poll::Ready(Some(Err(err))),
54            Poll::Pending => Poll::Pending,
55        }
56    }
57
58    fn size_hint(&self) -> http_body_1::SizeHint {
59        let size_hint = self.body.size_hint();
60        let mut out = http_body_1::SizeHint::new();
61        out.set_lower(size_hint.lower());
62        if let Some(upper) = size_hint.upper() {
63            out.set_upper(upper);
64        }
65        out
66    }
67
68    #[inline]
69    fn is_end_stream(&self) -> bool {
70        self.body.is_end_stream()
71    }
72}
73
74// --- http-body 1.0 to http-body 0.4 ---
75
76pin_project! {
77    /// Converts an [http-body 1.0 `Body`] to an [http-body 0.4 `Body`].
78    ///
79    /// [http-body 0.4 `Body`]: https://docs.rs/http-body/latest/http_body/trait.Body.html
80    /// [http-body 1.0 `Body`]: https://docs.rs/http-body/1.0.0-rc.2/http_body/trait.Body.html
81    #[derive(Debug, Clone, Default)]
82    pub struct HttpBody1ToHttpBody04<B> {
83        #[pin]
84        body: B,
85        trailers: Option<HeaderMap>,
86    }
87}
88
89impl<B> HttpBody1ToHttpBody04<B> {
90    /// Create a new `HttpBody1ToHttpBody04`.
91    #[inline]
92    pub fn new(body: B) -> Self {
93        Self {
94            body,
95            trailers: None,
96        }
97    }
98}
99
100impl<B> http_body_04::Body for HttpBody1ToHttpBody04<B>
101where
102    B: http_body_1::Body,
103{
104    type Data = B::Data;
105    type Error = B::Error;
106
107    fn poll_data(
108        self: Pin<&mut Self>,
109        cx: &mut Context<'_>,
110    ) -> Poll<Option<Result<Self::Data, Self::Error>>> {
111        let this = self.project();
112        match ready!(this.body.poll_frame(cx)) {
113            Some(Ok(frame)) => {
114                let frame = match frame.into_data() {
115                    Ok(data) => return Poll::Ready(Some(Ok(data))),
116                    Err(frame) => frame,
117                };
118
119                match frame.into_trailers() {
120                    Ok(trailers) => {
121                        *this.trailers = Some(trailers);
122                    }
123                    Err(_frame) => {}
124                }
125
126                Poll::Ready(None)
127            }
128            Some(Err(err)) => Poll::Ready(Some(Err(err))),
129            None => Poll::Ready(None),
130        }
131    }
132
133    fn poll_trailers(
134        mut self: Pin<&mut Self>,
135        cx: &mut Context<'_>,
136    ) -> Poll<Result<Option<http::HeaderMap>, Self::Error>> {
137        loop {
138            let this = self.as_mut().project();
139
140            if let Some(trailers) = this.trailers.take() {
141                break Poll::Ready(Ok(Some(trailers)));
142            }
143
144            match ready!(this.body.poll_frame(cx)) {
145                Some(Ok(frame)) => match frame.into_trailers() {
146                    Ok(trailers) => break Poll::Ready(Ok(Some(trailers))),
147                    // we might get a trailers frame on next poll
148                    // so loop and try again
149                    Err(_frame) => {}
150                },
151                Some(Err(err)) => break Poll::Ready(Err(err)),
152                None => break Poll::Ready(Ok(None)),
153            }
154        }
155    }
156
157    fn size_hint(&self) -> http_body_04::SizeHint {
158        let size_hint = self.body.size_hint();
159        let mut out = http_body_04::SizeHint::new();
160        out.set_lower(size_hint.lower());
161        if let Some(upper) = size_hint.upper() {
162            out.set_upper(upper);
163        }
164        out
165    }
166
167    #[inline]
168    fn is_end_stream(&self) -> bool {
169        self.body.is_end_stream()
170    }
171}