Skip to main content

serverkit/
stream.rs

1use std::{
2    fmt,
3    sync::Arc,
4    task::{Context, Poll},
5};
6
7use crate::{Error, IntoResponse, Response};
8
9pub trait RequestStream {
10    /// Advances to the next chunk without transferring its allocation.
11    ///
12    /// After returning `Poll::Ready(Some(Ok(())))`, `chunk` must expose the
13    /// new chunk until the next mutable call to this stream.
14    fn poll_next(&mut self, context: &mut Context<'_>) -> Poll<Option<Result<(), StreamError>>>;
15
16    /// Borrows the chunk produced by the latest successful `poll_next` call.
17    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}