1use std::{
2 fmt,
3 sync::Arc,
4 task::{Context, Poll},
5};
6
7use crate::{Error, IntoResponse, Response};
8
9pub trait RequestStream {
10 fn poll_next(&mut self, context: &mut Context<'_>) -> Poll<Option<Result<(), StreamError>>>;
15
16 fn chunk(&self) -> &[u8];
18}
19
20#[derive(Debug)]
21pub struct Chunk {
22 bytes: ChunkBytes,
23 position: usize,
24}
25
26#[derive(Debug)]
27enum ChunkBytes {
28 Owned(Vec<u8>),
29 Shared(Arc<Vec<u8>>),
30}
31
32impl Chunk {
33 pub fn shared(bytes: Arc<Vec<u8>>) -> Self {
34 Self {
35 bytes: ChunkBytes::Shared(bytes),
36 position: 0,
37 }
38 }
39
40 pub fn remaining(&self) -> usize {
41 let length = match &self.bytes {
42 ChunkBytes::Owned(bytes) => bytes.len(),
43 ChunkBytes::Shared(bytes) => bytes.len(),
44 };
45
46 length - self.position
47 }
48
49 pub fn bytes(&self) -> &[u8] {
50 let bytes = match &self.bytes {
51 ChunkBytes::Owned(bytes) => bytes,
52 ChunkBytes::Shared(bytes) => bytes,
53 };
54
55 &bytes[self.position..]
56 }
57
58 pub fn advance(&mut self, count: usize) {
59 assert!(count <= self.remaining(), "cannot advance past the chunk");
60 self.position += count;
61 }
62
63 pub fn into_vec(self) -> Vec<u8> {
64 let mut bytes = match self.bytes {
65 ChunkBytes::Owned(bytes) => bytes,
66 ChunkBytes::Shared(bytes) => match Arc::try_unwrap(bytes) {
67 Ok(bytes) => bytes,
68 Err(bytes) => return bytes[self.position..].to_vec(),
69 },
70 };
71
72 if self.position != 0 {
73 let remaining = bytes.len() - self.position;
74 bytes.copy_within(self.position.., 0);
75 bytes.truncate(remaining);
76 }
77
78 bytes
79 }
80}
81
82impl From<Vec<u8>> for Chunk {
83 fn from(bytes: Vec<u8>) -> Self {
84 Self {
85 bytes: ChunkBytes::Owned(bytes),
86 position: 0,
87 }
88 }
89}
90
91impl AsRef<[u8]> for Chunk {
92 fn as_ref(&self) -> &[u8] {
93 self.bytes()
94 }
95}
96
97pub trait ResponseStream {
98 fn poll_next(&mut self, context: &mut Context<'_>) -> Poll<Option<Result<Chunk, StreamError>>>;
99}
100
101#[derive(Debug)]
102pub struct StreamError {
103 status: u16,
104 code: &'static str,
105 message: String,
106}
107
108impl StreamError {
109 pub fn new(message: impl Into<String>) -> Self {
110 Self {
111 status: 400,
112 code: "request.body.invalid",
113 message: message.into(),
114 }
115 }
116
117 pub fn payload_too_large(limit: usize) -> Self {
118 Self {
119 status: 413,
120 code: "request.body.too_large",
121 message: format!("request body exceeds the {limit}-byte limit"),
122 }
123 }
124
125 pub fn message(&self) -> &str {
126 &self.message
127 }
128}
129
130impl fmt::Display for StreamError {
131 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132 formatter.write_str(&self.message)
133 }
134}
135
136impl std::error::Error for StreamError {}
137
138impl IntoResponse for StreamError {
139 fn into_response(self) -> Response {
140 Error::new(self.status, self.code, self.message).into_response()
141 }
142}
143
144pub(crate) async fn collect_stream(
145 stream: &mut dyn RequestStream,
146 limit: Option<usize>,
147) -> Result<Vec<u8>, StreamError> {
148 let mut buffered = Vec::new();
149
150 while let Some(result) = std::future::poll_fn(|context| stream.poll_next(context)).await {
151 result?;
152 let chunk = stream.chunk();
153
154 if let Some(limit) = limit
155 && buffered.len().saturating_add(chunk.len()) > limit
156 {
157 return Err(StreamError::payload_too_large(limit));
158 }
159
160 buffered.extend_from_slice(chunk);
161 }
162
163 Ok(buffered)
164}
165
166pub(crate) struct LimitedRequestStream {
167 stream: Box<dyn RequestStream>,
168 limit: usize,
169 read: usize,
170 exhausted: bool,
171}
172
173impl LimitedRequestStream {
174 pub(crate) fn new(stream: Box<dyn RequestStream>, limit: usize) -> Self {
175 Self {
176 stream,
177 limit,
178 read: 0,
179 exhausted: false,
180 }
181 }
182}
183
184impl RequestStream for LimitedRequestStream {
185 fn poll_next(&mut self, context: &mut Context<'_>) -> Poll<Option<Result<(), StreamError>>> {
186 if self.exhausted {
187 return Poll::Ready(None);
188 }
189
190 match self.stream.poll_next(context) {
191 Poll::Ready(Some(Ok(()))) => {
192 self.read = self.read.saturating_add(self.stream.chunk().len());
193
194 if self.read > self.limit {
195 self.exhausted = true;
196 Poll::Ready(Some(Err(StreamError::payload_too_large(self.limit))))
197 } else {
198 Poll::Ready(Some(Ok(())))
199 }
200 }
201 Poll::Ready(Some(Err(error))) => {
202 self.exhausted = true;
203 Poll::Ready(Some(Err(error)))
204 }
205 Poll::Ready(None) => {
206 self.exhausted = true;
207 Poll::Ready(None)
208 }
209 Poll::Pending => Poll::Pending,
210 }
211 }
212
213 fn chunk(&self) -> &[u8] {
214 self.stream.chunk()
215 }
216}
217
218pub(crate) struct BufferedRequestStream {
219 buffered: Vec<u8>,
220 consumed: bool,
221}
222
223impl BufferedRequestStream {
224 pub(crate) fn new(buffered: Vec<u8>) -> Self {
225 Self {
226 buffered,
227 consumed: false,
228 }
229 }
230}
231
232impl RequestStream for BufferedRequestStream {
233 fn poll_next(&mut self, _context: &mut Context<'_>) -> Poll<Option<Result<(), StreamError>>> {
234 if self.consumed || self.buffered.is_empty() {
235 return Poll::Ready(None);
236 }
237
238 self.consumed = true;
239 Poll::Ready(Some(Ok(())))
240 }
241
242 fn chunk(&self) -> &[u8] {
243 &self.buffered
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use std::sync::Arc;
250
251 use super::Chunk;
252
253 #[test]
254 fn owned_chunks_move_their_allocation() {
255 let bytes = b"chunk".to_vec();
256 let pointer = bytes.as_ptr();
257 let chunk = Chunk::from(bytes);
258 let bytes = chunk.into_vec();
259
260 assert_eq!(bytes.as_ptr(), pointer);
261 assert_eq!(bytes, b"chunk");
262 }
263
264 #[test]
265 fn chunks_support_multiple_partial_advances() {
266 let mut chunk = Chunk::from(b"abcdef".to_vec());
267
268 assert_eq!(chunk.remaining(), 6);
269 assert_eq!(chunk.bytes(), b"abcdef");
270
271 chunk.advance(2);
272 assert_eq!(chunk.remaining(), 4);
273 assert_eq!(chunk.bytes(), b"cdef");
274
275 chunk.advance(1);
276 assert_eq!(chunk.remaining(), 3);
277 assert_eq!(chunk.bytes(), b"def");
278
279 chunk.advance(3);
280 assert_eq!(chunk.remaining(), 0);
281 assert_eq!(chunk.bytes(), b"");
282 }
283
284 #[test]
285 fn shared_chunks_copy_only_when_converted_back_to_a_vec() {
286 let shared = Arc::new(b"abcdef".to_vec());
287 let mut chunk = Chunk::shared(Arc::clone(&shared));
288 chunk.advance(2);
289
290 assert_eq!(chunk.into_vec(), b"cdef");
291 assert_eq!(shared.as_slice(), b"abcdef");
292 }
293}