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