Skip to main content

leyline/core/
body.rs

1use std::fmt;
2use std::io;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use bytes::Bytes;
7use futures_util::Stream;
8
9pub(crate) type BoxedStream = Pin<Box<dyn Stream<Item = io::Result<Bytes>> + Send + 'static>>;
10
11#[derive(Default)]
12pub(crate) enum BodyKind {
13    #[default]
14    Empty,
15    Bytes(Bytes),
16    Stream {
17        stream: BoxedStream,
18        length_hint: Option<u64>,
19    },
20}
21
22#[derive(Default)]
23pub struct Body(pub(crate) BodyKind);
24
25impl fmt::Debug for Body {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        let mut out = f.debug_struct("Body");
28        match &self.0 {
29            BodyKind::Empty => out.field("kind", &"empty"),
30            BodyKind::Bytes(b) => out.field("kind", &"bytes").field("len", &b.len()),
31            BodyKind::Stream { length_hint, .. } => out
32                .field("kind", &"stream")
33                .field("length_hint", length_hint),
34        }
35        .finish()
36    }
37}
38
39impl Stream for Body {
40    type Item = io::Result<Bytes>;
41
42    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
43        match &mut self.0 {
44            BodyKind::Empty => Poll::Ready(None),
45            BodyKind::Bytes(_) => match std::mem::take(&mut self.0) {
46                BodyKind::Bytes(b) => Poll::Ready(Some(Ok(b))),
47                _ => Poll::Ready(None),
48            },
49            BodyKind::Stream { stream, .. } => stream.as_mut().poll_next(cx),
50        }
51    }
52}
53
54const EXCEEDED_DECLARED: &str = "streaming body exceeded declared content-length";
55const ENDED_BEFORE_DECLARED: &str = "streaming body ended before declared content-length";
56
57struct Declared {
58    inner: BoxedStream,
59    declared: u64,
60    sent: u64,
61    done: bool,
62}
63
64impl Declared {
65    fn fail(&mut self, message: String) -> Poll<Option<io::Result<Bytes>>> {
66        self.done = true;
67        Poll::Ready(Some(Err(io::Error::new(
68            io::ErrorKind::InvalidInput,
69            message,
70        ))))
71    }
72}
73
74impl Stream for Declared {
75    type Item = io::Result<Bytes>;
76
77    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
78        if self.done {
79            return Poll::Ready(None);
80        }
81        match self.inner.as_mut().poll_next(cx) {
82            Poll::Pending => Poll::Pending,
83            Poll::Ready(Some(Ok(chunk))) => {
84                let sent = self.sent.saturating_add(chunk.len() as u64);
85                if sent > self.declared {
86                    return self.fail(EXCEEDED_DECLARED.to_owned());
87                }
88                self.sent = sent;
89                Poll::Ready(Some(Ok(chunk)))
90            }
91            Poll::Ready(Some(Err(err))) => {
92                self.done = true;
93                Poll::Ready(Some(Err(err)))
94            }
95            Poll::Ready(None) if self.sent < self.declared => {
96                let message = format!("{ENDED_BEFORE_DECLARED} ({}/{})", self.sent, self.declared);
97                self.fail(message)
98            }
99            Poll::Ready(None) => {
100                self.done = true;
101                Poll::Ready(None)
102            }
103        }
104    }
105}
106
107impl Body {
108    pub(crate) fn bytes(bytes: Bytes) -> Self {
109        Body(BodyKind::Bytes(bytes))
110    }
111
112    pub fn stream<S>(stream: S, length: Option<u64>) -> Self
113    where
114        S: Stream<Item = io::Result<Bytes>> + Send + 'static,
115    {
116        let stream: BoxedStream = match length {
117            Some(declared) => Box::pin(Declared {
118                inner: Box::pin(stream),
119                declared,
120                sent: 0,
121                done: false,
122            }),
123            None => Box::pin(stream),
124        };
125        Body(BodyKind::Stream {
126            stream,
127            length_hint: length,
128        })
129    }
130
131    pub fn len_hint(&self) -> Option<u64> {
132        match &self.0 {
133            BodyKind::Empty => Some(0),
134            BodyKind::Bytes(b) => Some(b.len() as u64),
135            BodyKind::Stream { length_hint, .. } => *length_hint,
136        }
137    }
138
139    #[cfg(feature = "http3")]
140    pub(crate) fn is_stream(&self) -> bool {
141        matches!(self.0, BodyKind::Stream { .. })
142    }
143
144    #[cfg(feature = "http3")]
145    pub(crate) fn into_parts(self) -> (Option<Bytes>, Option<BoxedStream>) {
146        match self.0 {
147            BodyKind::Empty => (None, None),
148            BodyKind::Bytes(b) => (Some(b), None),
149            BodyKind::Stream { stream, .. } => (None, Some(stream)),
150        }
151    }
152
153    pub(crate) fn into_h2(self) -> crate::h2::client::RequestBody {
154        use crate::h2::client::RequestBody;
155        match self.0 {
156            BodyKind::Empty => RequestBody::None,
157            BodyKind::Bytes(b) => RequestBody::Buffered(b),
158            BodyKind::Stream {
159                stream,
160                length_hint,
161            } => RequestBody::Streaming {
162                stream,
163                length_hint,
164            },
165        }
166    }
167
168    pub(crate) fn replay(&self) -> Option<Body> {
169        match &self.0 {
170            BodyKind::Empty => Some(Body::default()),
171            BodyKind::Bytes(b) => Some(Body::bytes(b.clone())),
172            BodyKind::Stream { .. } => None,
173        }
174    }
175}
176
177impl From<Bytes> for Body {
178    fn from(b: Bytes) -> Self {
179        if b.is_empty() {
180            Body::default()
181        } else {
182            Body::bytes(b)
183        }
184    }
185}
186
187impl From<Vec<u8>> for Body {
188    fn from(v: Vec<u8>) -> Self {
189        Body::from(Bytes::from(v))
190    }
191}
192
193impl From<&'static [u8]> for Body {
194    fn from(v: &'static [u8]) -> Self {
195        Body::from(Bytes::from_static(v))
196    }
197}
198
199impl From<String> for Body {
200    fn from(s: String) -> Self {
201        Body::from(Bytes::from(s.into_bytes()))
202    }
203}
204
205impl From<&'static str> for Body {
206    fn from(s: &'static str) -> Self {
207        Body::from(Bytes::from_static(s.as_bytes()))
208    }
209}
210
211impl From<()> for Body {
212    fn from(_: ()) -> Self {
213        Body::default()
214    }
215}
216
217#[cfg(test)]
218mod tests;