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