aws_smithy_legacy_http_server/
body.rs1#[doc(hidden)]
10pub use http_body::Body as HttpBody;
11
12pub use hyper::body::Body;
13
14use bytes::Bytes;
15
16use crate::error::{BoxError, Error};
17
18pub type BoxBody = http_body::combinators::UnsyncBoxBody<Bytes, Error>;
20
21pub fn boxed<B>(body: B) -> BoxBody
24where
25 B: http_body::Body<Data = Bytes> + Send + 'static,
26 B::Error: Into<BoxError>,
27{
28 try_downcast(body).unwrap_or_else(|body| body.map_err(Error::new).boxed_unsync())
29}
30
31#[doc(hidden)]
32pub(crate) fn try_downcast<T, K>(k: K) -> Result<T, K>
33where
34 T: 'static,
35 K: Send + 'static,
36{
37 let mut k = Some(k);
38 if let Some(k) = <dyn std::any::Any>::downcast_mut::<Option<T>>(&mut k) {
39 Ok(k.take().unwrap())
40 } else {
41 Err(k.unwrap())
42 }
43}
44
45pub(crate) fn empty() -> BoxBody {
46 boxed(http_body::Empty::new())
47}
48
49#[doc(hidden)]
52pub fn to_boxed<B>(body: B) -> BoxBody
53where
54 Body: From<B>,
55{
56 boxed(Body::from(body))
57}
58
59use std::fmt;
64use std::future::poll_fn;
65use std::pin::Pin;
66
67#[doc(hidden)]
70#[derive(Debug, Clone, Copy)]
71pub struct BodyLimitExceeded {
72 pub limit: usize,
74}
75
76impl fmt::Display for BodyLimitExceeded {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(
79 f,
80 "request body exceeded the configured maximum of {} bytes",
81 self.limit
82 )
83 }
84}
85
86impl std::error::Error for BodyLimitExceeded {}
87
88#[doc(hidden)]
94#[derive(Debug)]
95pub enum CollectBodyError<E> {
96 Body(E),
98 TooLarge(BodyLimitExceeded),
100}
101
102impl<E: fmt::Display> fmt::Display for CollectBodyError<E> {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 match self {
105 Self::Body(e) => write!(f, "error reading request body: {e}"),
106 Self::TooLarge(e) => e.fmt(f),
107 }
108 }
109}
110
111impl<E: std::error::Error + 'static> std::error::Error for CollectBodyError<E> {
112 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
113 match self {
114 Self::Body(e) => Some(e),
115 Self::TooLarge(e) => Some(e),
116 }
117 }
118}
119
120#[doc(hidden)]
130pub async fn collect_body_limited<B>(body: B, limit: usize) -> Result<Bytes, CollectBodyError<B::Error>>
131where
132 B: HttpBody,
133{
134 let lower = body.size_hint().lower() as usize;
137 if lower > limit && limit > 0 {
138 return Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit }));
139 }
140
141 let mut body: Pin<Box<B>> = Box::pin(body);
142 let mut buf: Vec<u8> = Vec::with_capacity(lower);
143
144 loop {
145 let chunk_opt = poll_fn(|cx| body.as_mut().poll_data(cx)).await;
146 match chunk_opt {
147 None => break,
148 Some(Err(e)) => return Err(CollectBodyError::Body(e)),
149 Some(Ok(mut chunk)) => {
150 use bytes::Buf;
151 let chunk_len = chunk.remaining();
152 if limit > 0 && buf.len().saturating_add(chunk_len) > limit {
153 return Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit }));
154 }
155 buf.reserve(chunk_len);
156 while chunk.has_remaining() {
157 let slice = chunk.chunk();
158 let len = slice.len();
159 buf.extend_from_slice(slice);
160 chunk.advance(len);
161 }
162 }
163 }
164 }
165
166 Ok(Bytes::from(buf))
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[tokio::test]
174 async fn test_collect_body_limited_under_limit() {
175 let body = Body::from("hello");
176 let result = collect_body_limited(body, 1024).await;
177 assert_eq!(result.unwrap(), Bytes::from("hello"));
178 }
179
180 #[tokio::test]
181 async fn test_collect_body_limited_over_limit() {
182 let body = Body::from("this is way too long");
183 let result = collect_body_limited(body, 5).await;
184 assert!(matches!(
185 result,
186 Err(CollectBodyError::TooLarge(BodyLimitExceeded { limit: 5 }))
187 ));
188 }
189
190 #[tokio::test]
191 async fn test_collect_body_limited_chunked_exceeds_mid_stream() {
192 use futures_util::stream;
193
194 let chunks: Vec<Result<&[u8], std::io::Error>> = vec![
196 Ok(b"aaaa"), Ok(b"bbbb"), ];
199 let body = Body::wrap_stream(stream::iter(chunks));
200 let result = collect_body_limited(body, 6).await;
201 assert!(matches!(result, Err(CollectBodyError::TooLarge(_))));
202 }
203
204 #[tokio::test]
205 async fn test_collect_body_limited_empty_body() {
206 let body = Body::empty();
207 let result = collect_body_limited(body, 100).await;
208 assert_eq!(result.unwrap(), Bytes::from(""));
209 }
210}