1use crate::buffer::StreamBuffer;
5use crate::delimiter::{DataSearch, make_delimiter_finder, make_first_boundary, search_data};
6use crate::header::HeaderBlock;
7use crate::part::Part;
8use crate::part_data_stream::PartDataStream;
9use crate::{Boundary, Error};
10
11use std::future::poll_fn;
12use std::task::{Context, Poll, ready};
13
14use bytes::Bytes;
15use futures_core::Stream;
16use memchr::memmem;
17
18pub struct Multipart<S>
29where
30 S: Stream<Item = Result<Bytes, Error>> + Send + Unpin,
31{
32 buffer: Option<StreamBuffer<S>>,
38 first_boundary: Box<[u8]>,
39 delimiter_finder: Option<Box<memmem::Finder<'static>>>,
43 pub(super) max_buffer_size: usize,
44 pub(super) state: State,
45 pub(super) headers: Option<HeaderBlock>,
46 pub(super) part_handed_out: bool,
55}
56
57impl<S> Multipart<S>
58where
59 S: Stream<Item = Result<Bytes, Error>> + Send + Unpin,
60{
61 pub fn new(stream: S, boundary: &Boundary, max_buffer_size: usize) -> Self {
76 let first_boundary = make_first_boundary(boundary.as_bytes());
77 let delimiter_finder = make_delimiter_finder(boundary.as_bytes());
78 Self {
79 buffer: Some(StreamBuffer::new(stream)),
80 first_boundary,
81 delimiter_finder: Some(delimiter_finder),
82 max_buffer_size,
83 state: State::FindingFirstBoundary,
84 headers: None,
85 part_handed_out: false,
86 }
87 }
88
89 pub async fn next_part(&mut self) -> Result<Option<Part<'_, S>>, Error> {
101 loop {
102 match self.state {
103 State::StreamTaken => return Err(Error::StreamAlreadyTaken),
104 State::Done => return Ok(None),
105 State::FindingFirstBoundary => {
106 poll_fn(|cx| self.poll_advance_first_boundary(cx)).await?;
107 }
108 State::ReadingPartHeaders => {
109 if self.part_handed_out {
110 poll_fn(|cx| self.poll_ensure_headers(cx)).await?;
113 self.finish_headers();
114 continue;
115 }
116 self.part_handed_out = true;
117 return Ok(Some(Part::new(self)));
118 }
119 State::ReadingPartData => {
120 poll_fn(|cx| self.poll_skip_part_data(cx)).await?;
121 poll_fn(|cx| self.poll_advance_after_boundary(cx)).await?;
122 }
123 State::ReadingBoundary => {
124 poll_fn(|cx| self.poll_advance_after_boundary(cx)).await?;
125 }
126 State::ReadingClosingDelimiter => {
127 poll_fn(|cx| self.poll_advance_closing(cx)).await?;
128 }
129 State::ReadingEpilogue => {
130 poll_fn(|cx| self.poll_epilogue(cx)).await?;
131 }
132 }
133 }
134 }
135
136 pub fn poll_next_part(&mut self, cx: &mut Context<'_>) -> Poll<Result<Option<Part<'_, S>>, Error>> {
138 loop {
139 match self.state {
140 State::StreamTaken => return Poll::Ready(Err(Error::StreamAlreadyTaken)),
141 State::Done => return Poll::Ready(Ok(None)),
142 State::FindingFirstBoundary => {
143 ready!(self.poll_advance_first_boundary(cx)?);
144 }
145 State::ReadingPartHeaders => {
146 if self.part_handed_out {
147 ready!(self.poll_ensure_headers(cx)?);
149 self.finish_headers();
150 continue;
151 }
152 self.part_handed_out = true;
153 return Poll::Ready(Ok(Some(Part::new(self))));
154 }
155 State::ReadingPartData => {
156 ready!(self.poll_skip_part_data(cx)?);
157 ready!(self.poll_advance_after_boundary(cx)?);
158 }
159 State::ReadingBoundary => {
160 ready!(self.poll_advance_after_boundary(cx)?);
161 }
162 State::ReadingClosingDelimiter => {
163 ready!(self.poll_advance_closing(cx)?);
164 }
165 State::ReadingEpilogue => {
166 ready!(self.poll_epilogue(cx)?);
167 }
168 }
169 }
170 }
171
172 fn poll_advance_first_boundary(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
177 let Self {
182 buffer, first_boundary, ..
183 } = self;
184 let Some(buffer) = buffer.as_mut() else {
185 return Poll::Ready(Err(Error::StreamAlreadyTaken));
186 };
187 ready!(buffer.poll_read_to(first_boundary, cx)?);
188 self.state = State::ReadingBoundary;
189 Poll::Ready(Ok(()))
190 }
191
192 fn poll_advance_after_boundary(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
193 ready!(self.poll_ensure_buf_len(1, cx)?);
194 let first = match self.buffer_mut() {
195 Ok(buffer) => buffer.buf[0],
196 Err(err) => return Poll::Ready(Err(err)),
197 };
198 match first {
199 b'-' => {
200 ready!(self.poll_ensure_buf_len(2, cx)?);
201 let bytes = match self.buffer_mut() {
202 Ok(buffer) => &buffer.buf,
203 Err(err) => return Poll::Ready(Err(err)),
204 };
205 if bytes.get(..2) != Some(&b"--"[..]) {
206 return Poll::Ready(Err(Error::InvalidFormat));
207 }
208 if let Ok(buffer) = self.buffer_mut() {
209 let _ = buffer.buf.split_to(2);
210 }
211 self.state = State::ReadingClosingDelimiter;
212 Poll::Ready(Ok(()))
213 }
214 b' ' | b'\t' => {
215 ready!(self.poll_consume_padding_and_crlf(cx)?);
216 self.state = State::ReadingPartHeaders;
217 Poll::Ready(Ok(()))
218 }
219 b'\r' => {
220 ready!(self.poll_consume_crlf(cx)?);
221 self.state = State::ReadingPartHeaders;
222 Poll::Ready(Ok(()))
223 }
224 _ => Poll::Ready(Err(Error::InvalidFormat)),
225 }
226 }
227
228 fn poll_advance_closing(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
234 ready!(self.poll_consume_padding_and_crlf(cx)?);
235 self.state = State::ReadingEpilogue;
236 Poll::Ready(Ok(()))
237 }
238
239 fn poll_epilogue(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
242 let buffer = match self.buffer_mut() {
243 Ok(buffer) => buffer,
244 Err(err) => return Poll::Ready(Err(err)),
245 };
246 if !buffer.buf.is_empty() {
247 return Poll::Ready(Err(Error::StreamPartNotLast));
248 }
249 match ready!(buffer.poll_stream(cx)) {
250 None => {
251 self.state = State::Done;
252 Poll::Ready(Ok(()))
253 }
254 Some(Ok(_)) => Poll::Ready(Err(Error::StreamPartNotLast)),
258 Some(Err(err)) => Poll::Ready(Err(err)),
259 }
260 }
261
262 fn poll_consume_padding_and_crlf(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
263 loop {
264 ready!(self.poll_ensure_buf_len(1, cx)?);
265 let first = match self.buffer_mut() {
266 Ok(buffer) => buffer.buf[0],
267 Err(err) => return Poll::Ready(Err(err)),
268 };
269 if first == b'\r' {
270 return self.poll_consume_crlf(cx);
271 }
272 if !matches!(first, b' ' | b'\t') {
273 return Poll::Ready(Err(Error::InvalidFormat));
274 }
275
276 let padding_len = match self.buffer_mut() {
277 Ok(buffer) => buffer.buf.iter().take_while(|&&b| matches!(b, b' ' | b'\t')).count(),
278 Err(err) => return Poll::Ready(Err(err)),
279 };
280 if let Ok(buffer) = self.buffer_mut() {
281 let _ = buffer.buf.split_to(padding_len);
282 }
283 }
284 }
285
286 fn poll_consume_crlf(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
287 ready!(self.poll_ensure_buf_len(2, cx)?);
288 let bytes = match self.buffer_mut() {
289 Ok(buffer) => &buffer.buf,
290 Err(err) => return Poll::Ready(Err(err)),
291 };
292 if bytes.get(..2) != Some(&b"\r\n"[..]) {
293 return Poll::Ready(Err(Error::InvalidFormat));
294 }
295 if let Ok(buffer) = self.buffer_mut() {
296 let _ = buffer.buf.split_to(2);
297 }
298 Poll::Ready(Ok(()))
299 }
300
301 fn poll_ensure_buf_len(&mut self, len: usize, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
302 while match self.buffer_mut() {
303 Ok(buffer) => buffer.buf.len() < len,
304 Err(err) => return Poll::Ready(Err(err)),
305 } {
306 let buffer = match self.buffer_mut() {
307 Ok(buffer) => buffer,
308 Err(err) => return Poll::Ready(Err(err)),
309 };
310 match ready!(buffer.poll_stream(cx)) {
311 Some(Ok(chunk)) => {
312 if let Ok(buffer) = self.buffer_mut() {
313 buffer.buf.extend_from_slice(&chunk);
314 }
315 }
316 Some(Err(err)) => return Poll::Ready(Err(err)),
317 None => return Poll::Ready(Err(Error::IncompleteStream)),
318 }
319 }
320 Poll::Ready(Ok(()))
321 }
322
323 pub(super) fn poll_next_part_data_chunk(&mut self, cx: &mut Context<'_>) -> Poll<Result<Option<Bytes>, Error>> {
324 if self.state != State::ReadingPartData {
325 return Poll::Ready(Ok(None));
326 }
327
328 let Some(delimiter_finder) = self.delimiter_finder.as_deref() else {
331 return Poll::Ready(Ok(None));
332 };
333 let delimiter_len = delimiter_finder.needle().len();
334
335 loop {
336 {
337 let Some(buffer) = self.buffer.as_mut() else {
338 return Poll::Ready(Err(Error::StreamAlreadyTaken));
339 };
340 if !buffer.buf.is_empty() {
341 match search_data(&buffer.buf, delimiter_finder) {
342 DataSearch::Found { index } => {
343 let data = buffer.buf.split_to(index).freeze();
344 let _ = buffer.buf.split_to(delimiter_len);
345 self.state = State::ReadingBoundary;
346 return if data.is_empty() {
347 Poll::Ready(Ok(None))
348 } else {
349 Poll::Ready(Ok(Some(data)))
350 };
351 }
352 DataSearch::Emit { end } => {
353 let data = buffer.buf.split_to(end).freeze();
354 return Poll::Ready(Ok(Some(data)));
355 }
356 DataSearch::KeepAll => {}
357 }
358 }
359 }
360
361 let Some(buffer) = self.buffer.as_mut() else {
362 return Poll::Ready(Err(Error::StreamAlreadyTaken));
363 };
364 match ready!(buffer.poll_stream(cx)) {
365 Some(Ok(chunk)) => {
366 let Some(buffer) = self.buffer.as_mut() else {
367 return Poll::Ready(Err(Error::StreamAlreadyTaken));
368 };
369 if !buffer.buf.is_empty() {
370 buffer.buf.extend_from_slice(&chunk);
371 continue;
372 }
373
374 match search_data(&chunk, delimiter_finder) {
375 DataSearch::Found { index } => {
376 let data = chunk.slice(..index);
377 buffer.buf.clear();
378 buffer.buf.extend_from_slice(&chunk[index.saturating_add(delimiter_len)..]);
379 self.state = State::ReadingBoundary;
380 return if data.is_empty() {
381 Poll::Ready(Ok(None))
382 } else {
383 Poll::Ready(Ok(Some(data)))
384 };
385 }
386 DataSearch::Emit { end } => {
387 let data = chunk.slice(..end);
388 buffer.buf.extend_from_slice(&chunk[end..]);
389 return Poll::Ready(Ok(Some(data)));
390 }
391 DataSearch::KeepAll => {
392 buffer.buf.extend_from_slice(&chunk);
393 }
394 }
395 }
396 Some(Err(err)) => return Poll::Ready(Err(err)),
397 None => return Poll::Ready(Err(Error::IncompleteStream)),
398 }
399 }
400 }
401
402 fn poll_skip_part_data(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Error>> {
403 loop {
404 match ready!(self.poll_next_part_data_chunk(cx)) {
405 Ok(Some(_)) => {}
406 Ok(None) => return Poll::Ready(Ok(())),
407 Err(err) => return Poll::Ready(Err(err)),
408 }
409 }
410 }
411
412 pub(super) fn take_data_stream(&mut self) -> Result<PartDataStream<S>, Error> {
413 if self.state == State::ReadingPartHeaders {
414 self.finish_headers();
415 }
416 if self.state != State::ReadingPartData {
417 return Err(Error::InvalidFormat);
418 }
419
420 let Some(buffer) = self.buffer.take() else {
421 return Err(Error::StreamAlreadyTaken);
422 };
423 let Some(delimiter_finder) = self.delimiter_finder.take() else {
428 return Err(Error::StreamAlreadyTaken);
429 };
430 let multipart_consumed = buffer.consumed();
431 self.state = State::StreamTaken;
432
433 Ok(PartDataStream::new(buffer, delimiter_finder, multipart_consumed))
434 }
435
436 pub(super) fn buffer_mut(&mut self) -> Result<&mut StreamBuffer<S>, Error> {
437 self.buffer.as_mut().ok_or(Error::StreamAlreadyTaken)
438 }
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq)]
442pub enum State {
447 FindingFirstBoundary,
448 ReadingPartHeaders,
449 ReadingPartData,
450 ReadingBoundary,
451 ReadingClosingDelimiter,
452 ReadingEpilogue,
454 Done,
455 StreamTaken,
456}
457
458#[cfg(test)]
459#[allow(clippy::expect_used, clippy::panic, clippy::unreachable, clippy::unwrap_used)]
460mod tests {
461 use super::*;
462
463 use futures::executor::block_on;
464 use futures_util::StreamExt;
465 use futures_util::stream;
466
467 fn parser(payload: &'static [u8]) -> Multipart<impl Stream<Item = Result<Bytes, Error>> + Send + Sync> {
468 Multipart::new(
469 stream::iter([Ok::<Bytes, std::io::Error>(Bytes::from_static(payload))])
470 .map(|item| item.map_err(Error::stream_read_failed)),
471 &Boundary::new(b"boundary").unwrap(),
472 1024,
473 )
474 }
475
476 fn chunked_parser(chunks: Vec<&'static [u8]>) -> Multipart<impl Stream<Item = Result<Bytes, Error>> + Send + Sync> {
477 Multipart::new(
478 stream::iter(chunks.into_iter().map(|c| Ok::<Bytes, std::io::Error>(Bytes::from_static(c))))
479 .map(|item| item.map_err(Error::stream_read_failed)),
480 &Boundary::new(b"boundary").unwrap(),
481 1024,
482 )
483 }
484
485 fn owned_chunk_parser_with_max(
486 chunks: Vec<Vec<u8>>,
487 max: usize,
488 ) -> Multipart<impl Stream<Item = Result<Bytes, Error>> + Send + Sync> {
489 Multipart::new(
490 stream::iter(chunks.into_iter().map(|c| Ok::<Bytes, std::io::Error>(Bytes::from(c))))
491 .map(|item| item.map_err(Error::stream_read_failed)),
492 &Boundary::new(b"boundary").unwrap(),
493 max,
494 )
495 }
496
497 fn owned_chunk_parser(chunks: Vec<Vec<u8>>) -> Multipart<impl Stream<Item = Result<Bytes, Error>> + Send + Sync> {
498 owned_chunk_parser_with_max(chunks, 1024)
499 }
500
501 async fn drain_part<S>(part: &mut Part<'_, S>) -> (Vec<(Vec<u8>, Vec<u8>)>, Vec<u8>)
502 where
503 S: Stream<Item = Result<Bytes, Error>> + Send + Unpin,
504 {
505 let mut headers = Vec::new();
506 while let Some(header) = part.next_header().await.unwrap() {
507 headers.push((header.name.as_bytes().to_vec(), header.value.to_vec()));
508 }
509 let mut data = Vec::new();
510 while let Some(chunk) = part.next_data().await.unwrap() {
511 data.extend_from_slice(&chunk);
512 }
513 (headers, data)
514 }
515
516 #[test]
517 fn parses_preamble_headers_data_and_close() {
518 block_on(async {
519 let mut mp = parser(b"preamble\r\n--boundary\r\nX-Test: one\r\nContent-Disposition: form-data; name=\"file\"\r\n\r\nhello\r\n--boundary--\r\n");
520 let mut part = mp.next_part().await.unwrap().unwrap();
521 let (headers, data) = drain_part(&mut part).await;
522 assert_eq!(headers[0].0, b"X-Test");
523 assert_eq!(headers[0].1, b"one");
524 assert_eq!(headers[1].0, b"Content-Disposition");
525 assert_eq!(data, b"hello");
526 assert!(mp.next_part().await.unwrap().is_none());
527 });
528 }
529
530 #[test]
534 fn a_small_header_block_parses_at_every_split_position() {
535 block_on(async {
536 let mut body = b"--boundary\r\nA: b\r\n\r\n".to_vec();
537 body.extend(std::iter::repeat_n(b'x', 100));
538 body.extend_from_slice(b"\r\n--boundary--\r\n");
539
540 for split in 0..=body.len() {
543 let mut mp = owned_chunk_parser_with_max(vec![body[..split].to_vec(), body[split..].to_vec()], 32);
544 let mut part = mp
545 .next_part()
546 .await
547 .unwrap_or_else(|err| panic!("split {split}: {err}"))
548 .unwrap();
549 let mut headers = 0usize;
550 while part.next_header().await.unwrap().is_some() {
551 headers += 1;
552 }
553 let mut data = 0usize;
554 while let Some(chunk) = part.next_data().await.unwrap() {
555 data += chunk.len();
556 }
557 assert_eq!((headers, data), (1, 100), "split {split}");
558 assert!(mp.next_part().await.unwrap().is_none(), "split {split}");
559 }
560 });
561 }
562
563 #[test]
568 fn dropping_a_part_skips_it() {
569 block_on(async {
570 let body: &[u8] = b"--boundary\r\nX: a\r\n\r\nfirst\r\n--boundary\r\nX: b\r\n\r\nsecond\r\n--boundary--\r\n";
571
572 let mut mp = owned_chunk_parser(vec![body.to_vec()]);
574 let mut visits = 0usize;
575 loop {
576 {
577 let Some(part) = mp.next_part().await.unwrap() else { break };
579 let _ = part;
580 }
581 visits += 1;
582 assert!(visits <= 2, "next_part re-delivered a dropped part");
583 }
584 assert_eq!(visits, 2);
585
586 let mut mp = owned_chunk_parser(vec![body.to_vec()]);
588 let mut seen = Vec::new();
589 while let Some(mut part) = mp.next_part().await.unwrap() {
590 let header = part.next_header().await.unwrap().unwrap();
591 seen.push(String::from_utf8(header.value.to_vec()).unwrap());
592 assert!(seen.len() <= 2, "next_part re-delivered a partly read part");
593 }
594 assert_eq!(seen, ["a", "b"]);
595 });
596 }
597
598 #[test]
602 fn dropping_a_part_with_a_malformed_header_block_reports_the_error() {
603 block_on(async {
604 let mut mp = owned_chunk_parser(vec![b"--boundary\r\n\x01\x02bad\r\n\r\nDATA\r\n--boundary--\r\n".to_vec()]);
605 {
606 let part = mp.next_part().await.unwrap().unwrap();
608 let _ = part;
609 }
610 assert!(matches!(mp.next_part().await, Err(Error::InvalidFormat)));
611 });
612 }
613
614 #[test]
619 fn empty_header_block_parses_at_every_split_position() {
620 block_on(async {
621 for (body, expected) in [
622 (&b"--boundary\r\n\r\nDATA\r\n--boundary--\r\n"[..], vec![(0usize, &b"DATA"[..])]),
623 (
624 &b"--boundary\r\nX: y\r\n\r\nA\r\n--boundary\r\n\r\nDATA\r\n--boundary--\r\n"[..],
626 vec![(1usize, &b"A"[..]), (0usize, &b"DATA"[..])],
627 ),
628 ] {
629 let want: Vec<(usize, Vec<u8>)> = expected.iter().map(|(n, data)| (*n, data.to_vec())).collect();
630 for split in 0..=body.len() {
635 let mut mp = owned_chunk_parser(vec![body[..split].to_vec(), body[split..].to_vec()]);
636 let mut seen: Vec<(usize, Vec<u8>)> = Vec::new();
637 while let Some(mut part) = mp
638 .next_part()
639 .await
640 .unwrap_or_else(|err| panic!("next_part failed at split {split}: {err}"))
641 {
642 let (headers, data) = drain_part(&mut part).await;
643 seen.push((headers.len(), data));
644 }
645 assert_eq!(seen, want, "parts at split {split}");
646 }
647 }
648 });
649 }
650
651 #[test]
654 fn empty_chunks_after_the_closing_delimiter_are_not_an_epilogue() {
655 block_on(async {
656 let mut mp = owned_chunk_parser(vec![b"--boundary\r\n\r\nDATA\r\n--boundary--\r\n".to_vec(), Vec::new(), Vec::new()]);
657 let mut part = mp.next_part().await.unwrap().unwrap();
658 assert!(part.next_header().await.unwrap().is_none());
659 let mut data = Vec::new();
660 while let Some(chunk) = part.next_data().await.unwrap() {
661 data.extend_from_slice(&chunk);
662 }
663 assert_eq!(data, b"DATA");
664 assert!(mp.next_part().await.unwrap().is_none());
665 });
666 }
667
668 #[test]
673 fn an_endless_run_of_empty_chunks_is_a_stream_failure() {
674 struct BodyThenEmptyForever {
675 body: Option<Bytes>,
676 }
677
678 impl Stream for BodyThenEmptyForever {
679 type Item = Result<Bytes, Error>;
680
681 fn poll_next(mut self: std::pin::Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
682 match self.body.take() {
683 Some(body) => Poll::Ready(Some(Ok(body))),
684 None => Poll::Ready(Some(Ok(Bytes::new()))),
685 }
686 }
687 }
688
689 block_on(async {
690 let mut mp = Multipart::new(
691 BodyThenEmptyForever {
692 body: Some(Bytes::from_static(b"--boundary\r\n\r\nDATA\r\n--boundary--\r\n")),
693 },
694 &Boundary::new(b"boundary").unwrap(),
695 4096,
696 );
697 let mut part = mp.next_part().await.unwrap().unwrap();
698 assert!(part.next_header().await.unwrap().is_none());
699 let mut data = Vec::new();
700 while let Some(chunk) = part.next_data().await.unwrap() {
701 data.extend_from_slice(&chunk);
702 }
703 assert_eq!(data, b"DATA");
704 assert!(matches!(mp.next_part().await, Err(Error::StreamReadFailed(_))));
705 });
706
707 block_on(async {
708 let mut mp = Multipart::new(
710 BodyThenEmptyForever {
711 body: Some(Bytes::from_static(b"--boundary\r\nA: b\r\n")),
712 },
713 &Boundary::new(b"boundary").unwrap(),
714 4096,
715 );
716 let mut part = mp.next_part().await.unwrap().unwrap();
717 assert!(matches!(part.next_header().await, Err(Error::StreamReadFailed(_))));
718 });
719 }
720
721 #[test]
722 fn skips_unread_part_data_when_dropped() {
723 block_on(async {
724 let mut mp = parser(b"--boundary\r\n\r\nskipped data\r\n--boundary\r\nX: y\r\n\r\nnext\r\n--boundary--\r\n");
725 {
726 let mut part = mp.next_part().await.unwrap().unwrap();
727 let header = part.next_header().await.unwrap();
728 assert!(header.is_none());
729 }
731 let mut part = mp.next_part().await.unwrap().unwrap();
732 let (headers, data) = drain_part(&mut part).await;
733 assert_eq!(headers[0].1, b"y");
734 assert_eq!(data, b"next");
735 });
736 }
737
738 #[test]
739 fn handles_boundary_split_across_chunks() {
740 block_on(async {
741 let mut mp = chunked_parser(vec![
742 b"--boundary\r\n\r\nabc\r\n--boun",
743 b"dary\r\nX: y\r\n\r\nz\r\n--boundary--\r\n",
744 ]);
745 let mut part = mp.next_part().await.unwrap().unwrap();
746 let (_, data) = drain_part(&mut part).await;
747 assert_eq!(data, b"abc");
748 let mut part = mp.next_part().await.unwrap().unwrap();
749 let (_, data) = drain_part(&mut part).await;
750 assert_eq!(data, b"z");
751 });
752 }
753
754 #[test]
755 fn accepts_transport_padding_after_boundary() {
756 block_on(async {
757 let mut mp = parser(b"--boundary \t\r\nX: y\r\n\r\ndata\r\n--boundary \r\n\r\nnext\r\n--boundary--\t\r\n");
758 let mut part = mp.next_part().await.unwrap().unwrap();
759 let (_, data) = drain_part(&mut part).await;
760 assert_eq!(data, b"data");
761 let mut part = mp.next_part().await.unwrap().unwrap();
762 let (_, data) = drain_part(&mut part).await;
763 assert_eq!(data, b"next");
764 assert!(mp.next_part().await.unwrap().is_none());
765 });
766 }
767
768 #[test]
769 fn accepts_empty_header_and_empty_body_parts() {
770 block_on(async {
771 let mut mp = parser(b"--boundary\r\n\r\n\r\n--boundary\r\nX: y\r\n\r\n\r\n--boundary--\r\n");
772 let mut part = mp.next_part().await.unwrap().unwrap();
773 let (headers, data) = drain_part(&mut part).await;
774 assert_eq!(headers.len(), 0);
775 assert_eq!(data, b"");
776 let mut part = mp.next_part().await.unwrap().unwrap();
777 let (headers, data) = drain_part(&mut part).await;
778 assert_eq!(headers[0].1, b"y");
779 assert_eq!(data, b"");
780 });
781 }
782
783 #[test]
784 fn rejects_epilogue_after_close() {
785 block_on(async {
786 let mut mp = parser(b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundary--\r\nepilogue");
787 let mut part = mp.next_part().await.unwrap().unwrap();
788 let _ = drain_part(&mut part).await;
789 let err = mp.next_part().await.unwrap_err();
790 assert!(matches!(err, Error::StreamPartNotLast));
791 });
792 }
793
794 #[test]
795 fn rejects_missing_first_boundary() {
796 block_on(async {
797 let mut mp = parser(b"not multipart");
798 let err = mp.next_part().await.unwrap_err();
799 assert!(matches!(err, Error::InvalidFormat));
800 });
801 }
802
803 #[test]
804 fn enforces_header_size_limit() {
805 block_on(async {
806 let mut mp = Multipart::new(
807 stream::iter([Ok::<Bytes, std::io::Error>(Bytes::from_static(b"--boundary\r\nX: y\r\n\r\n"))])
808 .map(|item| item.map_err(Error::stream_read_failed)),
809 &Boundary::new(b"boundary").unwrap(),
810 4,
811 );
812 let mut part = mp.next_part().await.unwrap().unwrap();
813 let err = part.next_header().await.unwrap_err();
814 assert!(matches!(err, Error::HeaderSizeExceeded { limit: 4 }));
815 });
816 }
817
818 async fn collect_taken<St>(mut stream: St) -> Result<Vec<u8>, Error>
819 where
820 St: Stream<Item = Result<Bytes, Error>> + Unpin,
821 {
822 let mut data = Vec::new();
823 while let Some(item) = futures_util::StreamExt::next(&mut stream).await {
824 data.extend_from_slice(&item?);
825 }
826 Ok(data)
827 }
828
829 #[test]
830 fn take_data_stream_yields_data_and_validates_trailer() {
831 block_on(async {
832 let mut mp = parser(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--\r\n");
833 let mut part = mp.next_part().await.unwrap().unwrap();
834 while part.next_header().await.unwrap().is_some() {}
835 let mut stream = part.take_data_stream().unwrap();
836 let snapshot = stream.multipart_consumed();
837 assert!(snapshot > 0);
838 let data = collect_taken(&mut stream).await.unwrap();
839 assert_eq!(data, b"hello");
840 let final_stream = stream.into_final();
841 assert!(collect_taken(final_stream).await.is_ok());
842 assert!(matches!(mp.next_part().await, Err(Error::StreamAlreadyTaken)));
843 });
844 }
845
846 #[test]
847 fn take_data_stream_rejects_epilogue() {
848 block_on(async {
849 let mut mp = parser(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--\r\nepilogue");
850 let mut part = mp.next_part().await.unwrap().unwrap();
851 while part.next_header().await.unwrap().is_some() {}
852 let stream = part.take_data_stream().unwrap();
853 let err = collect_taken(stream.into_final()).await.unwrap_err();
854 assert!(matches!(err, Error::StreamPartNotLast));
855 });
856 }
857
858 #[test]
859 fn take_data_stream_rejects_second_part() {
860 block_on(async {
861 let mut mp = parser(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary\r\nX: z\r\n\r\nworld\r\n--boundary--\r\n");
862 let mut part = mp.next_part().await.unwrap().unwrap();
863 while part.next_header().await.unwrap().is_some() {}
864 let stream = part.take_data_stream().unwrap();
865 let err = collect_taken(stream.into_final()).await.unwrap_err();
866 assert!(matches!(err, Error::StreamPartNotLast));
867 });
868 }
869
870 #[test]
871 fn take_data_stream_reports_incomplete_trailer() {
872 block_on(async {
873 let mut mp = parser(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--");
874 let mut part = mp.next_part().await.unwrap().unwrap();
875 while part.next_header().await.unwrap().is_some() {}
876 let stream = part.take_data_stream().unwrap();
877 let err = collect_taken(stream.into_final()).await.unwrap_err();
878 assert!(matches!(err, Error::IncompleteStreamPart));
879 });
880 }
881
882 #[test]
886 fn take_data_stream_requires_a_parsed_header_block() {
887 block_on(async {
888 let mut mp = parser(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--\r\n");
889 let part = mp.next_part().await.unwrap().unwrap();
890 assert!(matches!(part.take_data_stream(), Err(Error::InvalidFormat)));
891 });
892 }
893}
894
895#[cfg(test)]
896#[allow(
897 clippy::expect_used,
898 clippy::indexing_slicing,
899 clippy::panic,
900 clippy::unreachable,
901 clippy::unwrap_used
902)]
903mod coverage_tests {
904 use super::*;
905
906 use crate::delimiter::make_delimiter;
907 use crate::header::MAX_HEADERS;
908
909 use std::io;
910
911 use futures::executor::block_on;
912 use futures_util::StreamExt;
913 use futures_util::stream;
914
915 fn parser_from_chunks(
916 chunks: Vec<Result<&'static [u8], io::Error>>,
917 ) -> Multipart<impl Stream<Item = Result<Bytes, Error>> + Send + Sync> {
918 Multipart::new(
919 stream::iter(
920 chunks
921 .into_iter()
922 .map(|item| item.map(Bytes::from_static).map_err(Error::stream_read_failed)),
923 ),
924 &Boundary::new(b"boundary").unwrap(),
925 4096,
926 )
927 }
928
929 fn ok_chunks(chunks: Vec<&'static [u8]>) -> Multipart<impl Stream<Item = Result<Bytes, Error>> + Send + Sync> {
930 parser_from_chunks(chunks.into_iter().map(Ok).collect())
931 }
932
933 async fn collect_taken<St>(mut stream: St) -> Result<Vec<u8>, Error>
934 where
935 St: Stream<Item = Result<Bytes, Error>> + Unpin,
936 {
937 let mut data = Vec::new();
938 while let Some(item) = futures_util::StreamExt::next(&mut stream).await {
939 data.extend_from_slice(&item?);
940 }
941 Ok(data)
942 }
943
944 #[test]
945 fn next_part_drives_closing_state() {
946 block_on(async {
947 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundary--\r\n")]);
948 let mut part = mp.next_part().await.unwrap().unwrap();
949 while part.next_data().await.unwrap().is_some() {}
950 let _ = part;
951 assert!(mp.next_part().await.unwrap().is_none());
952 });
953 }
954
955 #[test]
956 fn closing_trailer_error_paths() {
957 block_on(async {
958 for body in [
959 &b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundary-x\r\n"[..],
960 b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundaryX\r\n",
961 b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundary--x\r\n",
962 ] {
963 let mut mp = parser_from_chunks(vec![Ok(body)]);
964 let mut part = mp.next_part().await.unwrap().unwrap();
965 while part.next_data().await.unwrap().is_some() {}
966 let _ = part;
967 assert!(matches!(mp.next_part().await, Err(Error::InvalidFormat)), "{body:?}");
968 }
969
970 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\rx")]);
971 assert!(matches!(mp.next_part().await, Err(Error::InvalidFormat)));
972 });
973 }
974
975 #[test]
976 fn closing_poll_sees_separate_epilogue_and_errors() {
977 block_on(async {
978 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundary--\r\n"), Ok(b"epilogue")]);
979 let mut part = mp.next_part().await.unwrap().unwrap();
980 while part.next_data().await.unwrap().is_some() {}
981 let _ = part;
982 assert!(matches!(mp.next_part().await, Err(Error::StreamPartNotLast)));
983
984 let mut mp = parser_from_chunks(vec![
985 Ok(b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundary--\r\n"),
986 Err(io::Error::other("boom")),
987 ]);
988 let mut part = mp.next_part().await.unwrap().unwrap();
989 while part.next_data().await.unwrap().is_some() {}
990 let _ = part;
991 assert!(matches!(mp.next_part().await, Err(Error::StreamReadFailed(_))));
992 });
993 }
994
995 #[test]
996 fn first_boundary_ensure_buf_cross_chunk_and_eof() {
997 block_on(async {
998 let mut mp = ok_chunks(vec![b"--boundary", b"\r\nX: y\r\n\r\ndata\r\n--boundary--\r\n"]);
999 let mut part = mp.next_part().await.unwrap().unwrap();
1000 let mut data = Vec::new();
1001 while let Some(chunk) = part.next_data().await.unwrap() {
1002 data.extend_from_slice(&chunk);
1003 }
1004 assert_eq!(data, b"data");
1005
1006 let mut mp = ok_chunks(vec![b"--boundary"]);
1007 assert!(matches!(mp.next_part().await, Err(Error::IncompleteStream)));
1008 });
1009 }
1010
1011 #[test]
1012 fn next_header_after_data_returns_none() {
1013 block_on(async {
1014 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\ndata\r\n--boundary--\r\n")]);
1015 let mut part = mp.next_part().await.unwrap().unwrap();
1016 while part.next_header().await.unwrap().is_some() {}
1017 let _ = part.next_data().await.unwrap();
1018 assert!(part.next_header().await.unwrap().is_none());
1019 });
1020 }
1021
1022 #[test]
1023 fn next_data_direct_chunk_paths() {
1024 block_on(async {
1025 let mut mp = ok_chunks(vec![
1026 b"--boundary\r\n\r\n",
1027 b"abcdefghijklmnopqrstuvwxyz0123456789",
1028 b"more-data\r\n--boundary--\r\n",
1029 ]);
1030 let mut part = mp.next_part().await.unwrap().unwrap();
1031 let mut data = Vec::new();
1032 while let Some(chunk) = part.next_data().await.unwrap() {
1033 data.extend_from_slice(&chunk);
1034 }
1035 assert!(data.starts_with(b"abcdefghijklmnopqrstuvwxyz0123456789more-data"));
1036
1037 let mut mp = ok_chunks(vec![b"--boundary\r\n\r\n", b"\r\n--boundary\r\nX: y\r\n\r\nz\r\n--boundary--\r\n"]);
1038 let mut part = mp.next_part().await.unwrap().unwrap();
1039 assert!(part.next_data().await.unwrap().is_none());
1042
1043 let mut mp = ok_chunks(vec![b"--boundary\r\nX: y\r\n\r\n", b"abc"]);
1044 let mut part = mp.next_part().await.unwrap().unwrap();
1045 while part.next_header().await.unwrap().is_some() {}
1046 assert!(matches!(part.next_data().await, Err(Error::IncompleteStream)));
1047
1048 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\n"), Err(io::Error::other("boom"))]);
1049 let mut part = mp.next_part().await.unwrap().unwrap();
1050 while part.next_header().await.unwrap().is_some() {}
1051 assert!(matches!(part.next_data().await, Err(Error::StreamReadFailed(_))));
1052 });
1053 }
1054
1055 #[test]
1056 fn double_take_is_rejected() {
1057 block_on(async {
1058 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--\r\n")]);
1059 let mut part = mp.next_part().await.unwrap().unwrap();
1060 while part.next_header().await.unwrap().is_some() {}
1061 let _ = part.take_data_stream().unwrap();
1062 mp.state = State::ReadingPartData;
1063 mp.buffer = None;
1064 assert!(matches!(mp.take_data_stream(), Err(Error::StreamAlreadyTaken)));
1065 });
1066 }
1067
1068 #[test]
1069 fn debug_impls_render() {
1070 block_on(async {
1071 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--\r\n")]);
1072 let mut part = mp.next_part().await.unwrap().unwrap();
1073 assert!(format!("{part:?}").contains("Part"));
1074 while part.next_header().await.unwrap().is_some() {}
1075 let stream = part.take_data_stream().unwrap();
1076 assert!(format!("{stream:?}").contains("PartDataStream"));
1077 assert_eq!(stream.size_hint(), (0, None));
1078 });
1079 }
1080
1081 #[test]
1082 fn part_data_stream_poll_paths() {
1083 block_on(async {
1084 let mut mp = ok_chunks(vec![
1085 b"--boundary\r\nX: y\r\n\r\n",
1086 b"abcdefghijklmnopqrstuvwxyz0123456789",
1087 b"rest\r\n--boundary--\r\n",
1088 ]);
1089 let mut part = mp.next_part().await.unwrap().unwrap();
1090 while part.next_header().await.unwrap().is_some() {}
1091 let stream = part.take_data_stream().unwrap();
1092 let data = collect_taken(stream).await.unwrap();
1093 assert!(data.starts_with(b"abcdefghijklmnopqrstuvwxyz0123456789rest"));
1094
1095 let mut mp = ok_chunks(vec![b"--boundary\r\nX: y\r\n\r\n", b"\r\n--boundary--\r\n"]);
1096 let mut part = mp.next_part().await.unwrap().unwrap();
1097 while part.next_header().await.unwrap().is_some() {}
1098 let stream = part.take_data_stream().unwrap();
1099 let mut chunks = 0;
1100 let mut stream = Box::pin(stream);
1101 while let Some(item) = futures_util::StreamExt::next(&mut stream).await {
1102 item.unwrap();
1103 chunks += 1;
1104 }
1105 assert_eq!(chunks, 0);
1106
1107 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\n"), Err(io::Error::other("boom"))]);
1108 let mut part = mp.next_part().await.unwrap().unwrap();
1109 while part.next_header().await.unwrap().is_some() {}
1110 let stream = part.take_data_stream().unwrap();
1111 assert!(matches!(collect_taken(stream.into_final()).await, Err(Error::StreamReadFailed(_))));
1112
1113 let mut mp = ok_chunks(vec![b"--boundary\r\nX: y\r\n\r\nabc"]);
1114 let mut part = mp.next_part().await.unwrap().unwrap();
1115 while part.next_header().await.unwrap().is_some() {}
1116 let stream = part.take_data_stream().unwrap();
1117 assert!(matches!(collect_taken(stream).await, Err(Error::IncompleteStreamPart)));
1118 });
1119 }
1120
1121 #[test]
1122 fn part_data_stream_trailer_paths() {
1123 block_on(async {
1124 for body in [
1125 &b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary-x\r\n"[..],
1126 b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--x\r\n",
1127 ] {
1128 let mut mp = parser_from_chunks(vec![Ok(body)]);
1129 let mut part = mp.next_part().await.unwrap().unwrap();
1130 while part.next_header().await.unwrap().is_some() {}
1131 let stream = part.take_data_stream().unwrap();
1132 assert!(matches!(collect_taken(stream.into_final()).await, Err(Error::InvalidFormat)), "{body:?}");
1133 }
1134
1135 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary-- \t\r\n")]);
1136 let mut part = mp.next_part().await.unwrap().unwrap();
1137 while part.next_header().await.unwrap().is_some() {}
1138 let stream = part.take_data_stream().unwrap();
1139 assert_eq!(collect_taken(stream.into_final()).await.unwrap(), b"hello");
1140
1141 let mut mp = parser_from_chunks(vec![Ok(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--\r\n"), Ok(b"epilogue")]);
1142 let mut part = mp.next_part().await.unwrap().unwrap();
1143 while part.next_header().await.unwrap().is_some() {}
1144 let stream = part.take_data_stream().unwrap();
1145 assert!(matches!(collect_taken(stream.into_final()).await, Err(Error::StreamPartNotLast)));
1146
1147 let mut mp = parser_from_chunks(vec![
1148 Ok(b"--boundary\r\nX: y\r\n\r\nhello\r\n--boundary--\r\n"),
1149 Err(io::Error::other("boom")),
1150 ]);
1151 let mut part = mp.next_part().await.unwrap().unwrap();
1152 while part.next_header().await.unwrap().is_some() {}
1153 let stream = part.take_data_stream().unwrap();
1154 assert!(matches!(collect_taken(stream.into_final()).await, Err(Error::StreamReadFailed(_))));
1155 });
1156 }
1157
1158 #[test]
1159 fn next_data_yields_buffered_data_prefix() {
1160 block_on(async {
1161 let mut mp = ok_chunks(vec![b"--boundary\r\nX: y\r\n\r\n0123456789abcdef", b"rest\r\n--boundary--\r\n"]);
1162 let mut part = mp.next_part().await.unwrap().unwrap();
1163 while part.next_header().await.unwrap().is_some() {}
1164 let first = part.next_data().await.unwrap().unwrap();
1165 assert!(!first.is_empty());
1166 });
1167 }
1168
1169 #[test]
1170 fn data_chunk_exactly_keep_is_buffered_not_split() {
1171 block_on(async {
1172 let delimiter_len = make_delimiter(b"boundary").len();
1173 let keep = delimiter_len - 1;
1174 let prefix = vec![b'x'; keep];
1175 let mut chunks = vec![b"--boundary\r\n\r\n".to_vec()];
1176 chunks.push(prefix);
1177 chunks.push(b"tail\r\n--boundary--\r\n".to_vec());
1178 let items = chunks
1179 .into_iter()
1180 .map(|chunk| Ok::<Bytes, std::io::Error>(Bytes::from(chunk)))
1181 .collect::<Vec<_>>();
1182 let mut multipart = Multipart::new(
1183 stream::iter(items).map(|item| item.map_err(Error::stream_read_failed)),
1184 &Boundary::new(b"boundary").unwrap(),
1185 4096,
1186 );
1187 let mut part = multipart.next_part().await.unwrap().unwrap();
1188 while part.next_header().await.unwrap().is_some() {}
1189 let mut data_chunks = 0;
1190 while part.next_data().await.unwrap().is_some() {
1191 data_chunks += 1;
1192 }
1193 assert_eq!(data_chunks, 1);
1194
1195 let delimiter_len = make_delimiter(b"boundary").len();
1196 let keep = delimiter_len - 1;
1197 let prefix = vec![b'x'; keep];
1198 let mut chunks = vec![b"--boundary\r\nContent-Disposition: form-data; name=\"file\"\r\n\r\n".to_vec()];
1199 chunks.push(prefix);
1200 chunks.push(b"tail\r\n--boundary--\r\n".to_vec());
1201 let items = chunks
1202 .into_iter()
1203 .map(|chunk| Ok::<Bytes, std::io::Error>(Bytes::from(chunk)))
1204 .collect::<Vec<_>>();
1205 let mut multipart = Multipart::new(
1206 stream::iter(items).map(|item| item.map_err(Error::stream_read_failed)),
1207 &Boundary::new(b"boundary").unwrap(),
1208 4096,
1209 );
1210 let mut part = multipart.next_part().await.unwrap().unwrap();
1211 while part.next_header().await.unwrap().is_some() {}
1212 let mut stream = Box::pin(part.take_data_stream().unwrap());
1213 let mut data_chunks = 0;
1214 while let Some(item) = futures_util::StreamExt::next(&mut stream).await {
1215 item.unwrap();
1216 data_chunks += 1;
1217 }
1218 assert_eq!(data_chunks, 1);
1219 });
1220 }
1221
1222 #[test]
1226 fn fallback_headers_keep_names_and_values() {
1227 block_on(async {
1228 let mut body = Vec::new();
1229 body.extend_from_slice(b"--boundary\r\n");
1230 for idx in 0..33 {
1231 body.extend_from_slice(format!("X-{idx}: value-{idx}\r\n").as_bytes());
1232 }
1233 body.extend_from_slice(b"\r\ndata\r\n--boundary--\r\n");
1234 let mut multipart = Multipart::new(
1235 stream::iter([Ok::<Bytes, std::io::Error>(Bytes::from(body))])
1236 .map(|item| item.map_err(Error::stream_read_failed)),
1237 &Boundary::new(b"boundary").unwrap(),
1238 8192,
1239 );
1240 let mut part = multipart.next_part().await.unwrap().unwrap();
1241 let mut count = 0;
1242 while let Some(header) = part.next_header().await.unwrap() {
1243 assert_eq!(header.name, format!("X-{count}"));
1244 assert_eq!(header.value, format!("value-{count}").as_bytes());
1245 count += 1;
1246 }
1247 assert_eq!(count, MAX_HEADERS);
1248 });
1249 }
1250
1251 #[test]
1256 fn large_data_chunk_keeps_a_split_delimiter() {
1257 block_on(async {
1258 const BIG: usize = 5000;
1259
1260 let delimiter = b"\r\n--boundary";
1261 for split in 1..delimiter.len() {
1262 let mut first = b"--boundary\r\nX: y\r\n\r\n".to_vec();
1263 first.extend(std::iter::repeat_n(b'x', BIG));
1264 first.extend_from_slice(&delimiter[..split]);
1265 let mut second = delimiter[split..].to_vec();
1266 second.extend_from_slice(b"--\r\n");
1267
1268 let mut mp = Multipart::new(
1269 stream::iter([Ok::<Bytes, std::io::Error>(Bytes::from(first)), Ok(Bytes::from(second))])
1270 .map(|item| item.map_err(Error::stream_read_failed)),
1271 &Boundary::new(b"boundary").unwrap(),
1272 4096,
1273 );
1274 let mut part = mp.next_part().await.unwrap().unwrap();
1275 while part.next_header().await.unwrap().is_some() {}
1276 let mut stream = part.take_data_stream().unwrap();
1277
1278 let data = collect_taken(&mut stream).await.unwrap();
1279 assert_eq!(data.len(), BIG, "split={split}");
1280 assert!(data.iter().all(|byte| *byte == b'x'), "split={split}");
1281
1282 let final_stream = stream.into_final();
1283 assert!(collect_taken(final_stream).await.is_ok(), "split={split}");
1284 }
1285 });
1286 }
1287
1288 const TWO_PARTS: &[u8] = b"--boundary\r\nX: a\r\n\r\nalpha\r\n--boundary\r\nX: b\r\n\r\nbeta\r\n--boundary--\r\n";
1290
1291 async fn drain_all<S>(mp: &mut Multipart<S>) -> Result<Vec<(Vec<(String, Vec<u8>)>, Vec<u8>)>, Error>
1293 where
1294 S: Stream<Item = Result<Bytes, Error>> + Send + Unpin,
1295 {
1296 let mut parts = Vec::new();
1297 while let Some(mut part) = mp.next_part().await? {
1298 let mut headers = Vec::new();
1299 while let Some(header) = part.next_header().await? {
1300 headers.push((header.name.to_string(), header.value.to_vec()));
1301 }
1302 let mut data = Vec::new();
1303 while let Some(chunk) = part.next_data().await? {
1304 data.extend_from_slice(&chunk);
1305 }
1306 parts.push((headers, data));
1307 }
1308 Ok(parts)
1309 }
1310
1311 type ParsedPart = (Vec<(String, Vec<u8>)>, Vec<u8>);
1313
1314 fn drain_all_poll<S>(mp: &mut Multipart<S>, cx: &mut Context<'_>) -> Vec<ParsedPart>
1316 where
1317 S: Stream<Item = Result<Bytes, Error>> + Send + Unpin,
1318 {
1319 let mut parts = Vec::new();
1320 loop {
1321 match mp.poll_next_part(cx) {
1322 Poll::Pending => {}
1324 Poll::Ready(Ok(Some(mut part))) => parts.push(drain_part_poll(&mut part, cx)),
1325 Poll::Ready(Ok(None)) => break,
1326 Poll::Ready(Err(err)) => panic!("unexpected error: {err}"),
1327 }
1328 }
1329 parts
1330 }
1331
1332 fn drain_part_poll<S>(part: &mut Part<'_, S>, cx: &mut Context<'_>) -> ParsedPart
1333 where
1334 S: Stream<Item = Result<Bytes, Error>> + Send + Unpin,
1335 {
1336 let mut headers = Vec::new();
1337 loop {
1338 match part.poll_next_header(cx) {
1339 Poll::Pending => {}
1340 Poll::Ready(Ok(Some(header))) => headers.push((header.name.to_string(), header.value.to_vec())),
1341 Poll::Ready(Ok(None)) => break,
1342 Poll::Ready(Err(err)) => panic!("unexpected header error: {err}"),
1343 }
1344 }
1345 let mut data = Vec::new();
1346 loop {
1347 match part.poll_next_data(cx) {
1348 Poll::Pending => {}
1349 Poll::Ready(Ok(Some(chunk))) => data.extend_from_slice(&chunk),
1350 Poll::Ready(Ok(None)) => break,
1351 Poll::Ready(Err(err)) => panic!("unexpected data error: {err}"),
1352 }
1353 }
1354 (headers, data)
1355 }
1356
1357 fn pending_stream(
1360 chunks: Vec<Result<Bytes, Error>>,
1361 pending_before: usize,
1362 ) -> impl Stream<Item = Result<Bytes, Error>> + Send + Sync {
1363 let mut chunks = chunks.into_iter();
1364 let mut yielded = 0usize;
1365 stream::poll_fn(move |cx| {
1366 if yielded == pending_before {
1367 yielded += 1;
1368 cx.waker().wake_by_ref();
1369 return Poll::Pending;
1370 }
1371 yielded += 1;
1372 Poll::Ready(chunks.next())
1373 })
1374 }
1375
1376 #[test]
1379 fn poll_next_part_matches_the_async_path() {
1380 let reference = block_on(async {
1381 let mut mp = ok_chunks(vec![TWO_PARTS]);
1382 drain_all(&mut mp).await.unwrap()
1383 });
1384 assert_eq!(reference.len(), 2);
1385
1386 let mut mp = ok_chunks(vec![TWO_PARTS]);
1387 let waker = futures::task::noop_waker();
1388 let mut cx = Context::from_waker(&waker);
1389 assert_eq!(drain_all_poll(&mut mp, &mut cx), reference);
1390
1391 assert!(matches!(mp.poll_next_part(&mut cx), Poll::Ready(Ok(None))));
1393 assert!(matches!(mp.poll_next_part(&mut cx), Poll::Ready(Ok(None))));
1394 }
1395
1396 #[test]
1402 fn poll_next_part_skips_unread_part_data() {
1403 let reference = block_on(async {
1404 let mut mp = ok_chunks(vec![TWO_PARTS]);
1405 drain_all(&mut mp).await.unwrap()
1406 });
1407 assert_eq!(reference.len(), 2);
1408
1409 let mut mp = ok_chunks(vec![TWO_PARTS]);
1410 let waker = futures::task::noop_waker();
1411 let mut cx = Context::from_waker(&waker);
1412
1413 let mut headers_seen = Vec::new();
1414 loop {
1415 match mp.poll_next_part(&mut cx) {
1416 Poll::Pending => {}
1417 Poll::Ready(Ok(Some(mut part))) => {
1418 let mut headers = Vec::new();
1419 loop {
1420 match part.poll_next_header(&mut cx) {
1421 Poll::Pending => {}
1422 Poll::Ready(Ok(Some(header))) => headers.push((header.name.to_string(), header.value.to_vec())),
1423 Poll::Ready(Ok(None)) => break,
1424 Poll::Ready(Err(err)) => panic!("unexpected header error: {err}"),
1425 }
1426 }
1427 headers_seen.push(headers);
1430 }
1431 Poll::Ready(Ok(None)) => break,
1432 Poll::Ready(Err(err)) => panic!("unexpected error: {err}"),
1433 }
1434 }
1435
1436 let expected: Vec<_> = reference.into_iter().map(|(headers, _data)| headers).collect();
1437 assert_eq!(headers_seen, expected);
1438 }
1439
1440 #[test]
1444 fn skipping_unread_data_reports_a_truncated_part() {
1445 let truncated = &TWO_PARTS[..TWO_PARTS.len() - 7];
1448
1449 let async_err = block_on(async {
1450 let mut mp = ok_chunks(vec![truncated]);
1451 for _ in 0..2 {
1452 let mut part = mp.next_part().await.unwrap().unwrap();
1453 while part.next_header().await.unwrap().is_some() {}
1457 }
1458 mp.next_part().await.unwrap_err()
1459 });
1460 assert!(matches!(async_err, Error::IncompleteStream), "{async_err}");
1461
1462 let mut mp = ok_chunks(vec![truncated]);
1463 let waker = futures::task::noop_waker();
1464 let mut cx = Context::from_waker(&waker);
1465 let poll_err = loop {
1466 match mp.poll_next_part(&mut cx) {
1467 Poll::Pending => {}
1468 Poll::Ready(Ok(Some(mut part))) => while let Poll::Ready(Ok(Some(_))) = part.poll_next_header(&mut cx) {},
1469 Poll::Ready(Ok(None)) => panic!("a truncated body must not report the end"),
1470 Poll::Ready(Err(err)) => break err,
1471 }
1472 };
1473 assert!(matches!(poll_err, Error::IncompleteStream), "{poll_err}");
1474 }
1475
1476 #[test]
1479 fn poll_next_part_reports_a_taken_stream() {
1480 let mut mp = ok_chunks(vec![b"--boundary\r\nX: a\r\n\r\nalpha\r\n--boundary--\r\n"]);
1481 let waker = futures::task::noop_waker();
1482 let mut cx = Context::from_waker(&waker);
1483
1484 let mut part = match mp.poll_next_part(&mut cx) {
1485 Poll::Ready(Ok(Some(part))) => part,
1486 other => panic!("expected a part, got {other:?}"),
1487 };
1488 while let Poll::Ready(Ok(Some(_))) = part.poll_next_header(&mut cx) {}
1489 let _stream = part.take_data_stream().unwrap();
1490
1491 assert!(matches!(mp.poll_next_part(&mut cx), Poll::Ready(Err(Error::StreamAlreadyTaken))));
1492 }
1493
1494 #[test]
1499 fn accepts_a_stream_that_is_send_but_not_sync() {
1500 struct SendOnlyStream {
1501 _not_sync: std::cell::Cell<u8>,
1502 chunks: std::vec::IntoIter<Result<Bytes, Error>>,
1503 }
1504
1505 impl Stream for SendOnlyStream {
1506 type Item = Result<Bytes, Error>;
1507
1508 fn poll_next(mut self: std::pin::Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
1509 Poll::Ready(self.chunks.next())
1510 }
1511 }
1512
1513 let chunks: Vec<Result<Bytes, Error>> =
1514 vec![Ok(Bytes::from_static(b"--boundary\r\nX: a\r\n\r\nalpha\r\n--boundary--\r\n"))];
1515 let stream = SendOnlyStream {
1516 _not_sync: std::cell::Cell::new(0),
1517 chunks: chunks.into_iter(),
1518 };
1519 let boundary = Boundary::new(b"boundary").unwrap();
1520 let mut mp = Multipart::new(stream, &boundary, 4096);
1521 let parsed = block_on(drain_all(&mut mp)).unwrap();
1522 assert_eq!(parsed.len(), 1);
1523 assert_eq!(parsed[0].1, b"alpha");
1524 }
1525
1526 #[test]
1530 fn poll_next_part_resumes_after_pending_at_every_offset() {
1531 let reference = block_on(async {
1532 let mut mp = ok_chunks(vec![TWO_PARTS]);
1533 drain_all(&mut mp).await.unwrap()
1534 });
1535
1536 for pending_before in 0..=TWO_PARTS.len() {
1537 let chunks: Vec<Result<Bytes, Error>> = TWO_PARTS.chunks(1).map(|c| Ok(Bytes::copy_from_slice(c))).collect();
1538 let mut mp = Multipart::new(pending_stream(chunks, pending_before), &Boundary::new(b"boundary").unwrap(), 4096);
1539 let parsed = block_on(drain_all(&mut mp)).unwrap_or_else(|err| panic!("pending_before={pending_before}: {err}"));
1540 assert_eq!(parsed, reference, "pending_before={pending_before}");
1541 }
1542 }
1543
1544 #[test]
1548 fn truncated_or_failing_streams_error_at_every_offset() {
1549 for cut in 0..TWO_PARTS.len() {
1550 let truncated = stream::iter(vec![Ok::<Bytes, Error>(Bytes::copy_from_slice(&TWO_PARTS[..cut]))]);
1551 let mut mp = Multipart::new(truncated, &Boundary::new(b"boundary").unwrap(), 4096);
1552 assert!(block_on(drain_all(&mut mp)).is_err(), "truncated at {cut}");
1553
1554 let failing = stream::iter(vec![
1555 Ok::<Bytes, Error>(Bytes::copy_from_slice(&TWO_PARTS[..cut])),
1556 Err(Error::stream_read_failed(io::Error::other("boom"))),
1557 ]);
1558 let mut mp = Multipart::new(failing, &Boundary::new(b"boundary").unwrap(), 4096);
1559 assert!(block_on(drain_all(&mut mp)).is_err(), "failing at {cut}");
1560 }
1561 }
1562
1563 #[test]
1567 fn fresh_chunk_with_data_and_delimiter_yields_the_prefix() {
1568 block_on(async {
1569 let mut mp = ok_chunks(vec![b"--boundary\r\nX: y\r\n\r\n", b"hello\r\n--boundary--\r\n"]);
1570 let mut part = mp.next_part().await.unwrap().unwrap();
1571 while part.next_header().await.unwrap().is_some() {}
1572
1573 let first = part.next_data().await.unwrap().unwrap();
1574 assert_eq!(first.as_ref(), b"hello");
1575 assert!(part.next_data().await.unwrap().is_none());
1576 });
1577 }
1578}