• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #[cfg(test)]
15 #[macro_use]
16 #[rustfmt::skip]
17 pub(crate) mod mime_test_macro;
18 
19 mod common;
20 mod decode;
21 mod encode;
22 mod mimetype;
23 
24 use std::io::Cursor;
25 use std::pin::Pin;
26 use std::task::{Context, Poll};
27 use std::vec::IntoIter;
28 
29 pub(crate) use common::{
30     DecodeHeaders, EncodeHeaders, HeaderStatus, MixFrom, PartStatus, CR, CRLF, HTAB, LF, SP,
31 };
32 pub use common::{MimeMulti, MimeMultiBuilder, MimePart, MimePartBuilder, TokenStatus, XPart};
33 pub use decode::MimeMultiDecoder;
34 pub(crate) use decode::MimePartDecoder;
35 pub use encode::{MimeMultiEncoder, MimePartEncoder};
36 pub use mimetype::MimeType;
37 
38 // TODO: reuse mime later.
39 
40 // TODO: Adapter, remove this later.
41 use crate::body::async_impl::{poll_read, Body};
42 use crate::{AsyncRead, ReadBuf};
43 
44 /// A structure that helps you build a `multipart/form-data` message.
45 ///
46 /// # Examples
47 ///
48 /// ```
49 /// # use ylong_http::body::{MultiPart, Part};
50 ///
51 /// let multipart = MultiPart::new()
52 ///     .part(Part::new().name("name").body("xiaoming"))
53 ///     .part(Part::new().name("password").body("123456789"));
54 /// ```
55 pub struct MultiPart {
56     parts: Vec<Part>,
57     boundary: String,
58     status: ReadStatus,
59 }
60 
61 impl MultiPart {
62     /// Creates an empty `Multipart` with boundary created automatically.
63     ///
64     /// # Examples
65     ///
66     /// ```
67     /// # use ylong_http::body::MultiPart;
68     ///
69     /// let multipart = MultiPart::new();
70     /// ```
new() -> Self71     pub fn new() -> Self {
72         Self {
73             parts: Vec::new(),
74             boundary: gen_boundary(),
75             status: ReadStatus::Never,
76         }
77     }
78 
79     /// Sets a part to the `Multipart`.
80     ///
81     /// # Examples
82     ///
83     /// ```
84     /// # use ylong_http::body::{MultiPart, Part};
85     ///
86     /// let multipart = MultiPart::new().part(Part::new().name("name").body("xiaoming"));
87     /// ```
part(mut self, part: Part) -> Self88     pub fn part(mut self, part: Part) -> Self {
89         self.parts.push(part);
90         self
91     }
92 
93     /// Gets the boundary of this `Multipart`.
94     ///
95     /// # Examples
96     ///
97     /// ```
98     /// # use ylong_http::body::MultiPart;
99     ///
100     /// let multipart = MultiPart::new();
101     /// let boundary = multipart.boundary();
102     /// ```
boundary(&self) -> &str103     pub fn boundary(&self) -> &str {
104         self.boundary.as_str()
105     }
106 
107     /// Get the total bytes of the `multpart/form-data` message, including
108     /// length of every parts, such as boundaries, headers, bodies, etc.
109     ///
110     /// # Examples
111     ///
112     /// ```
113     /// # use ylong_http::body::{MultiPart, Part};
114     ///
115     /// let multipart = MultiPart::new().part(Part::new().name("name").body("xiaoming"));
116     ///
117     /// let bytes = multipart.total_bytes();
118     /// ```
total_bytes(&self) -> Option<u64>119     pub fn total_bytes(&self) -> Option<u64> {
120         let mut size = 0u64;
121         for part in self.parts.iter() {
122             size += part.length?;
123 
124             // start boundary + \r\n
125             size += 2 + self.boundary.len() as u64 + 2;
126 
127             // Content-Disposition: form-data
128             size += 30;
129 
130             // ; name="xxx"
131             if let Some(name) = part.name.as_ref() {
132                 size += 9 + name.len() as u64;
133             }
134 
135             // ; filename="xxx"
136             if let Some(name) = part.file_name.as_ref() {
137                 size += 13 + name.len() as u64;
138             }
139 
140             // \r\n
141             size += 2;
142 
143             // Content-Type: xxx
144             if let Some(mime) = part.mime.as_ref() {
145                 size += 16 + mime.len() as u64;
146             }
147 
148             // \r\n\r\n
149             size += 2 + 2;
150         }
151         // last boundary
152         size += 2 + self.boundary.len() as u64 + 4;
153         Some(size)
154     }
155 
build_status(&mut self)156     pub(crate) fn build_status(&mut self) {
157         let mut states = Vec::new();
158         for part in self.parts.iter_mut() {
159             states.push(MultiPartState::bytes(
160                 format!("--{}\r\n", self.boundary).into_bytes(),
161             ));
162             states.push(MultiPartState::bytes(
163                 b"Content-Disposition: form-data".to_vec(),
164             ));
165 
166             if let Some(ref name) = part.name {
167                 states.push(MultiPartState::bytes(
168                     format!("; name=\"{name}\"").into_bytes(),
169                 ));
170             }
171 
172             if let Some(ref file_name) = part.file_name {
173                 states.push(MultiPartState::bytes(
174                     format!("; filename=\"{file_name}\"").into_bytes(),
175                 ));
176             }
177 
178             states.push(MultiPartState::bytes(b"\r\n".to_vec()));
179 
180             if let Some(ref mime) = part.mime {
181                 states.push(MultiPartState::bytes(
182                     format!("Content-Type: {mime}\r\n").into_bytes(),
183                 ));
184             }
185 
186             states.push(MultiPartState::bytes(b"\r\n".to_vec()));
187 
188             if let Some(body) = part.body.take() {
189                 states.push(body);
190             }
191 
192             states.push(MultiPartState::bytes(b"\r\n".to_vec()));
193         }
194         states.push(MultiPartState::bytes(
195             format!("--{}--\r\n", self.boundary).into_bytes(),
196         ));
197         self.status = ReadStatus::Reading(MultiPartStates {
198             states: states.into_iter(),
199             curr: None,
200         })
201     }
202 }
203 
204 impl Default for MultiPart {
default() -> Self205     fn default() -> Self {
206         Self::new()
207     }
208 }
209 
210 impl AsyncRead for MultiPart {
poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<std::io::Result<()>>211     fn poll_read(
212         mut self: Pin<&mut Self>,
213         cx: &mut Context<'_>,
214         buf: &mut ReadBuf<'_>,
215     ) -> Poll<std::io::Result<()>> {
216         match self.status {
217             ReadStatus::Never => self.build_status(),
218             ReadStatus::Reading(_) => {}
219             ReadStatus::Finish => return Poll::Ready(Ok(())),
220         }
221 
222         if let ReadStatus::Reading(ref mut status) = self.status {
223             if buf.initialize_unfilled().is_empty() {
224                 return Poll::Ready(Ok(()));
225             }
226             let filled = buf.filled().len();
227             return match Pin::new(status).poll_read(cx, buf) {
228                 Poll::Ready(Ok(())) => {
229                     let new_filled = buf.filled().len();
230                     if filled == new_filled {
231                         self.status = ReadStatus::Finish;
232                     }
233                     Poll::Ready(Ok(()))
234                 }
235                 Poll::Pending => {
236                     let new_filled = buf.filled().len();
237                     return if new_filled != filled {
238                         Poll::Ready(Ok(()))
239                     } else {
240                         Poll::Pending
241                     };
242                 }
243                 x => x,
244             };
245         }
246         Poll::Ready(Ok(()))
247     }
248 }
249 
250 // TODO: Adapter, remove this later.
251 impl Body for MultiPart {
252     type Error = std::io::Error;
253 
poll_data( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll<Result<usize, Self::Error>>254     fn poll_data(
255         self: Pin<&mut Self>,
256         cx: &mut Context<'_>,
257         buf: &mut [u8],
258     ) -> Poll<Result<usize, Self::Error>> {
259         poll_read(self, cx, buf)
260     }
261 }
262 
263 /// A structure that represents a part of `multipart/form-data` message.
264 ///
265 /// # Examples
266 ///
267 /// ```
268 /// # use ylong_http::body::Part;
269 ///
270 /// let part = Part::new().name("name").body("xiaoming");
271 /// ```
272 pub struct Part {
273     name: Option<String>,
274     file_name: Option<String>,
275     mime: Option<String>,
276     length: Option<u64>,
277     body: Option<MultiPartState>,
278 }
279 
280 impl Part {
281     /// Creates an empty `Part`.
282     ///
283     /// # Examples
284     ///
285     /// ```
286     /// use ylong_http::body::Part;
287     ///
288     /// let part = Part::new();
289     /// ```
new() -> Self290     pub fn new() -> Self {
291         Self {
292             name: None,
293             file_name: None,
294             mime: None,
295             length: None,
296             body: None,
297         }
298     }
299 
300     /// Sets the name of this `Part`.
301     ///
302     /// The name message will be set to `Content-Disposition` header.
303     ///
304     /// # Examples
305     ///
306     /// ```
307     /// use ylong_http::body::Part;
308     ///
309     /// let part = Part::new().name("name");
310     /// ```
name(mut self, name: &str) -> Self311     pub fn name(mut self, name: &str) -> Self {
312         self.name = Some(String::from(name));
313         self
314     }
315 
316     /// Sets the file name of this `Part`.
317     ///
318     /// The file name message will be set to `Content-Disposition` header.
319     ///
320     /// # Examples
321     ///
322     /// ```
323     /// use ylong_http::body::Part;
324     ///
325     /// let part = Part::new().file_name("example.txt");
326     /// ```
file_name(mut self, file_name: &str) -> Self327     pub fn file_name(mut self, file_name: &str) -> Self {
328         self.file_name = Some(String::from(file_name));
329         self
330     }
331 
332     /// Sets the mime type of this `Part`.
333     ///
334     /// The mime type message will be set to `Content-Type` header.
335     ///
336     /// # Examples
337     ///
338     /// ```
339     /// use ylong_http::body::Part;
340     ///
341     /// let part = Part::new().mime("application/octet-stream");
342     /// ```
mime(mut self, mime: &str) -> Self343     pub fn mime(mut self, mime: &str) -> Self {
344         self.mime = Some(String::from(mime));
345         self
346     }
347 
348     /// Sets the length of body of this `Part`.
349     ///
350     /// The length message will be set to `Content-Length` header.
351     ///
352     /// # Examples
353     ///
354     /// ```
355     /// use ylong_http::body::Part;
356     ///
357     /// let part = Part::new().length(Some(8)).body("xiaoming");
358     /// ```
length(mut self, length: Option<u64>) -> Self359     pub fn length(mut self, length: Option<u64>) -> Self {
360         self.length = length;
361         self
362     }
363 
364     /// Sets a slice body of this `Part`.
365     ///
366     /// The body message will be set to the body part.
367     ///
368     /// # Examples
369     ///
370     /// ```
371     /// use ylong_http::body::Part;
372     ///
373     /// let part = Part::new().mime("application/octet-stream");
374     /// ```
body<T: AsRef<[u8]>>(mut self, body: T) -> Self375     pub fn body<T: AsRef<[u8]>>(mut self, body: T) -> Self {
376         let body = body.as_ref().to_vec();
377         self.length = Some(body.len() as u64);
378         self.body = Some(MultiPartState::bytes(body));
379         self
380     }
381 
382     /// Sets a stream body of this `Part`.
383     ///
384     /// The body message will be set to the body part.
stream<T: AsyncRead + Send + Sync + 'static>(mut self, body: T) -> Self385     pub fn stream<T: AsyncRead + Send + Sync + 'static>(mut self, body: T) -> Self {
386         self.body = Some(MultiPartState::stream(Box::pin(body)));
387         self
388     }
389 }
390 
391 impl Default for Part {
default() -> Self392     fn default() -> Self {
393         Self::new()
394     }
395 }
396 
397 impl AsRef<MultiPart> for MultiPart {
as_ref(&self) -> &MultiPart398     fn as_ref(&self) -> &MultiPart {
399         self
400     }
401 }
402 
403 enum ReadStatus {
404     Never,
405     Reading(MultiPartStates),
406     Finish,
407 }
408 
409 struct MultiPartStates {
410     states: IntoIter<MultiPartState>,
411     curr: Option<MultiPartState>,
412 }
413 
414 impl MultiPartStates {
poll_read_curr( &mut self, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<std::io::Result<()>>415     fn poll_read_curr(
416         &mut self,
417         cx: &mut Context<'_>,
418         buf: &mut ReadBuf<'_>,
419     ) -> Poll<std::io::Result<()>> {
420         if let Some(mut state) = self.curr.take() {
421             return match state {
422                 MultiPartState::Bytes(ref mut bytes) => {
423                     let filled_len = buf.filled().len();
424                     let unfilled = buf.initialize_unfilled();
425                     let unfilled_len = unfilled.len();
426                     let new = std::io::Read::read(bytes, unfilled).unwrap();
427                     buf.set_filled(filled_len + new);
428 
429                     if new >= unfilled_len {
430                         self.curr = Some(state);
431                     }
432                     Poll::Ready(Ok(()))
433                 }
434                 MultiPartState::Stream(ref mut stream) => {
435                     let old_len = buf.filled().len();
436                     match stream.as_mut().poll_read(cx, buf) {
437                         Poll::Ready(Ok(())) => {
438                             if old_len != buf.filled().len() {
439                                 self.curr = Some(state);
440                             }
441                             Poll::Ready(Ok(()))
442                         }
443                         Poll::Pending => {
444                             self.curr = Some(state);
445                             Poll::Pending
446                         }
447                         x => x,
448                     }
449                 }
450             };
451         }
452         Poll::Ready(Ok(()))
453     }
454 }
455 
456 impl AsyncRead for MultiPartStates {
poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<std::io::Result<()>>457     fn poll_read(
458         self: Pin<&mut Self>,
459         cx: &mut Context<'_>,
460         buf: &mut ReadBuf<'_>,
461     ) -> Poll<std::io::Result<()>> {
462         let this = self.get_mut();
463         while !buf.initialize_unfilled().is_empty() {
464             if this.curr.is_none() {
465                 this.curr = match this.states.next() {
466                     None => break,
467                     x => x,
468                 }
469             }
470 
471             match this.poll_read_curr(cx, buf) {
472                 Poll::Ready(Ok(())) => {}
473                 x => return x,
474             }
475         }
476         Poll::Ready(Ok(()))
477     }
478 }
479 
480 enum MultiPartState {
481     Bytes(Cursor<Vec<u8>>),
482     Stream(Pin<Box<dyn AsyncRead + Send + Sync>>),
483 }
484 
485 impl MultiPartState {
bytes(bytes: Vec<u8>) -> Self486     fn bytes(bytes: Vec<u8>) -> Self {
487         Self::Bytes(Cursor::new(bytes))
488     }
489 
stream(reader: Pin<Box<dyn AsyncRead + Send + Sync>>) -> Self490     fn stream(reader: Pin<Box<dyn AsyncRead + Send + Sync>>) -> Self {
491         Self::Stream(reader)
492     }
493 }
494 
495 #[cfg(test)]
496 impl PartialEq for MultiPartState {
eq(&self, other: &Self) -> bool497     fn eq(&self, other: &Self) -> bool {
498         match (self, other) {
499             (Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0,
500             // Cant not compare Stream, Should not do this.
501             (Self::Stream(l0), Self::Stream(r0)) => core::ptr::eq(l0, r0),
502             _ => false,
503         }
504     }
505 }
506 
507 #[cfg(test)]
508 impl core::fmt::Debug for MultiPartState {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result509     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
510         match self {
511             Self::Bytes(arg0) => f.debug_tuple("Bytes").field(arg0).finish(),
512             Self::Stream(arg0) => f.debug_tuple("Stream").field(&(arg0 as *const _)).finish(),
513         }
514     }
515 }
516 
gen_boundary() -> String517 fn gen_boundary() -> String {
518     format!(
519         "{:016x}-{:016x}-{:016x}-{:016x}",
520         xor_shift(),
521         xor_shift(),
522         xor_shift(),
523         xor_shift()
524     )
525 }
526 
527 // XORShift* fast-random realization.
xor_shift() -> u64528 fn xor_shift() -> u64 {
529     use std::cell::Cell;
530     use std::collections::hash_map::RandomState;
531     use std::hash::{BuildHasher, Hasher};
532     use std::num::Wrapping;
533 
534     thread_local! {
535         static RNG: Cell<Wrapping<u64>> = Cell::new(Wrapping(seed()));
536     }
537 
538     // The returned value of `seed()` must be nonzero.
539     fn seed() -> u64 {
540         let seed = RandomState::new();
541 
542         let mut out;
543         let mut cnt = 1;
544         let mut hasher = seed.build_hasher();
545 
546         loop {
547             hasher.write_usize(cnt);
548             out = hasher.finish();
549             if out != 0 {
550                 break;
551             }
552             cnt += 1;
553             hasher = seed.build_hasher();
554         }
555         out
556     }
557 
558     RNG.with(|rng| {
559         let mut n = rng.get();
560         n ^= n >> 12;
561         n ^= n << 25;
562         n ^= n >> 27;
563         rng.set(n);
564         n.0.wrapping_mul(0x2545_f491_4f6c_dd1d)
565     })
566 }
567 
568 #[cfg(test)]
569 mod ut_mime {
570     use crate::body::mime::{gen_boundary, MultiPartState, ReadStatus};
571     use crate::body::{MultiPart, Part};
572 
573     /// UT test cases for `gen_boundar`.
574     ///
575     /// # Brief
576     /// 1. Creates two boundarys and compares.
577     /// 3. Checks whether the result is correct.
578     #[test]
ut_gen_boundary()579     fn ut_gen_boundary() {
580         let s1 = gen_boundary();
581         let s2 = gen_boundary();
582         assert_ne!(s1, s2);
583     }
584 
585     /// UT test cases for `Part::new`.
586     ///
587     /// # Brief
588     /// 1. Creates a `Part` by `Part::new`.
589     /// 2. Checks members of `Part`.
590     /// 3. Checks whether the result is correct.
591     #[test]
ut_part_new()592     fn ut_part_new() {
593         let part = Part::new();
594         assert!(part.name.is_none());
595         assert!(part.file_name.is_none());
596         assert!(part.mime.is_none());
597         assert!(part.length.is_none());
598         assert!(part.body.is_none());
599     }
600 
601     /// UT test cases for `Part::default`.
602     ///
603     /// # Brief
604     /// 1. Creates a `Part` by `Part::default`.
605     /// 2. Checks members of `Part`.
606     /// 3. Checks whether the result is correct.
607     #[test]
ut_part_default()608     fn ut_part_default() {
609         let part = Part::default();
610         assert!(part.name.is_none());
611         assert!(part.file_name.is_none());
612         assert!(part.mime.is_none());
613         assert!(part.length.is_none());
614         assert!(part.body.is_none());
615     }
616 
617     /// UT test cases for `Part::name`, `Part::name`, `Part::file_name` and
618     /// `Part::body`.
619     ///
620     /// # Brief
621     /// 1. Creates a `Part` and sets values.
622     /// 2. Checks members of `Part`.
623     /// 3. Checks whether the result is correct.
624     #[test]
ut_part_set()625     fn ut_part_set() {
626         let part = Part::new()
627             .name("name")
628             .file_name("example.txt")
629             .mime("application/octet-stream")
630             .body("1234");
631         assert_eq!(part.name, Some("name".to_string()));
632         assert_eq!(part.file_name, Some("example.txt".to_string()));
633         assert_eq!(part.mime, Some("application/octet-stream".to_string()));
634         assert_eq!(part.body, Some(MultiPartState::bytes("1234".into())));
635         assert_eq!(part.length, Some(4));
636 
637         let part = part.stream("11223344".as_bytes()).length(Some(8));
638         assert_eq!(part.length, Some(8));
639     }
640 
641     /// UT test cases for `MultiPart::new`.
642     ///
643     /// # Brief
644     /// 1. Creates a `MultiPart` by `MultiPart::new`.
645     /// 2. Checks members of `MultiPart`.
646     /// 3. Checks whether the result is correct.
647     #[test]
ut_multipart_new()648     fn ut_multipart_new() {
649         let mp = MultiPart::new();
650         assert!(mp.parts.is_empty());
651         assert!(!mp.boundary().is_empty());
652     }
653 
654     /// UT test cases for `MultiPart::part` and `MultiPart::total_bytes`.
655     ///
656     /// # Brief
657     /// 1. Creates a `MultiPart` and sets values.
658     /// 2. Checks total bytes of `MultiPart`.
659     /// 3. Checks whether the result is correct.
660     #[test]
ut_multipart_set()661     fn ut_multipart_set() {
662         let mp = MultiPart::default();
663         // --boundary--/r/n
664         assert_eq!(mp.total_bytes(), Some(2 + mp.boundary().len() as u64 + 4));
665 
666         let mp = mp.part(
667             Part::new()
668                 .name("name")
669                 .file_name("example.txt")
670                 .mime("application/octet-stream")
671                 .body("1234"),
672         );
673         assert_eq!(
674             mp.total_bytes(),
675             Some(
676                 (2 + mp.boundary().len() as u64 + 2)
677                     + (30 + 9 + 4 + 13 + 11 + 2)       // name, filename, \r\n
678                     + (16 + 24 + 2 + 2)                // mime, \r\n
679                     + 4                                // body
680                     + (2 + mp.boundary().len() as u64 + 4)
681             )
682         );
683     }
684 
685     /// UT test cases for `MultiPart::poll_data`.
686     ///
687     /// # Brief
688     /// 1. Creates a `MultiPart` and sets values.
689     /// 2. Encodes `MultiPart` by `async_impl::Body::data`.
690     /// 3. Checks whether the result is correct.
691     #[cfg(feature = "ylong_base")]
692     #[test]
ut_multipart_poll_data()693     fn ut_multipart_poll_data() {
694         let handle = ylong_runtime::spawn(async move {
695             multipart_poll_data().await;
696         });
697         ylong_runtime::block_on(handle).unwrap();
698     }
699 
700     #[cfg(feature = "ylong_base")]
multipart_poll_data()701     async fn multipart_poll_data() {
702         let mut mp = MultiPart::new().part(
703             Part::new()
704                 .name("name")
705                 .file_name("example.txt")
706                 .mime("application/octet-stream")
707                 .body("1234"),
708         );
709 
710         let mut buf = vec![0u8; 50];
711         let mut v_size = vec![];
712         let mut v_str = vec![];
713 
714         loop {
715             let len = crate::body::async_impl::Body::data(&mut mp, &mut buf)
716                 .await
717                 .unwrap();
718             if len == 0 {
719                 break;
720             }
721             v_size.push(len);
722             v_str.extend_from_slice(&buf[..len]);
723         }
724         assert_eq!(v_size, vec![50, 50, 50, 50, 50, 11]);
725     }
726 
727     /// UT test cases for `MultiPart::poll_data`.
728     ///
729     /// # Brief
730     /// 1. Creates a `MultiPart` and sets values.
731     /// 2. Encodes `MultiPart` by `async_impl::Body::data`.
732     /// 3. Checks whether the result is correct.
733     #[cfg(feature = "ylong_base")]
734     #[test]
ut_multipart_poll_data_stream()735     fn ut_multipart_poll_data_stream() {
736         let handle = ylong_runtime::spawn(async move {
737             multipart_poll_data_stream().await;
738         });
739         ylong_runtime::block_on(handle).unwrap();
740     }
741 
742     #[cfg(feature = "ylong_base")]
multipart_poll_data_stream()743     async fn multipart_poll_data_stream() {
744         let mut mp = MultiPart::new().part(
745             Part::new()
746                 .name("name")
747                 .file_name("example.txt")
748                 .mime("application/octet-stream")
749                 .stream("1234".as_bytes())
750                 .length(Some(4)),
751         );
752 
753         let mut buf = vec![0u8; 50];
754         let mut v_size = vec![];
755         let mut v_str = vec![];
756 
757         loop {
758             let len = crate::body::async_impl::Body::data(&mut mp, &mut buf)
759                 .await
760                 .unwrap();
761             if len == 0 {
762                 break;
763             }
764             v_size.push(len);
765             v_str.extend_from_slice(&buf[..len]);
766         }
767         assert_eq!(v_size, vec![50, 50, 50, 50, 50, 11]);
768     }
769 }
770