xref: /xiu/protocol/rtmp/src/chunk/unpacketizer.rs (revision 5377b641)
1 use {
2     super::{
3         chunk::{ChunkBasicHeader, ChunkHeader, ChunkInfo, ChunkMessageHeader},
4         define,
5         errors::{UnpackError, UnpackErrorValue},
6     },
7     crate::{config, messages::define::msg_type_id, utils},
8     byteorder::{BigEndian, LittleEndian},
9     bytes::{BufMut, BytesMut},
10     chrono::prelude::*,
11     networkio::bytes_reader::BytesReader,
12     std::{cmp::min, collections::HashMap, vec::Vec},
13 };
14 
15 #[derive(Eq, PartialEq, Debug)]
16 pub enum UnpackResult {
17     ChunkBasicHeaderResult(ChunkBasicHeader),
18     ChunkMessageHeaderResult(ChunkMessageHeader),
19     ChunkInfo(ChunkInfo),
20     Chunks(Vec<ChunkInfo>),
21     Success,
22     NotEnoughBytes,
23     Empty,
24 }
25 
26 #[derive(Copy, Clone)]
27 enum ChunkReadState {
28     ReadBasicHeader = 1,
29     ReadMessageHeader = 2,
30     ReadExtendedTimestamp = 3,
31     ReadMessagePayload = 4,
32     Finish = 5,
33 }
34 
35 #[derive(Copy, Clone)]
36 enum MessageHeaderReadState {
37     ReadTimeStamp = 1,
38     ReadMsgLength = 2,
39     ReadMsgTypeID = 3,
40     ReadMsgStreamID = 4,
41 }
42 
43 fn f(chunk: &ChunkReadState) -> u8 {
44     *chunk as u8
45 }
46 
47 pub struct ChunkUnpacketizer {
48     pub reader: BytesReader,
49 
50     //https://doc.rust-lang.org/stable/rust-by-example/scope/lifetime/fn.html
51     //https://zhuanlan.zhihu.com/p/165976086
52     pub current_chunk_info: ChunkInfo,
53     chunk_headers: HashMap<u32, ChunkHeader>,
54     chunk_read_state: ChunkReadState,
55     msg_header_read_state: MessageHeaderReadState,
56     max_chunk_size: usize,
57     chunk_index: u32,
58     pub session_type: u8,
59 }
60 
61 impl ChunkUnpacketizer {
62     pub fn new() -> Self {
63         Self {
64             reader: BytesReader::new(BytesMut::new()),
65             current_chunk_info: ChunkInfo::default(),
66             chunk_headers: HashMap::new(),
67             chunk_read_state: ChunkReadState::ReadBasicHeader,
68             msg_header_read_state: MessageHeaderReadState::ReadTimeStamp,
69             max_chunk_size: define::INIT_CHUNK_SIZE as usize,
70             chunk_index: 0,
71             session_type: 0,
72         }
73     }
74 
75     pub fn extend_data(&mut self, data: &[u8]) {
76         self.reader.extend_from_slice(data);
77     }
78 
79     pub fn update_max_chunk_size(&mut self, chunk_size: usize) {
80         self.max_chunk_size = chunk_size;
81     }
82 
83     pub fn read_chunks(&mut self) -> Result<UnpackResult, UnpackError> {
84         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
85             print!("\n");
86             print!("\n");
87 
88             let dt = Local::now();
89             print!(
90                 "read_chunks begin=====+++++++++++++++++++++++++{} {}\n",
91                 dt.timestamp_nanos(),
92                 f(&self.chunk_read_state)
93             );
94         }
95 
96         if config::DEBUG && ((config::SERVER_PUSH & self.session_type) == 0){
97             print!("begin trunks data========\n");
98             utils::print::print(self.reader.get_remaining_bytes());
99             print!("end trunks data========\n");
100         }
101 
102         let mut chunks: Vec<ChunkInfo> = Vec::new();
103 
104         loop {
105             match self.read_chunk() {
106                 Ok(chunk) => match chunk {
107                     UnpackResult::ChunkInfo(chunk_info) => {
108                         let msg_type_id = chunk_info.message_header.msg_type_id.clone();
109                         chunks.push(chunk_info);
110 
111                         //if the chunk_size is changed, then break and update chunk_size
112                         if msg_type_id == msg_type_id::SET_CHUNK_SIZE {
113                             break;
114                         }
115                     }
116                     _ => continue,
117                 },
118                 Err(_) => break,
119             }
120         }
121 
122         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
123             let dt = Local::now();
124             print!(
125                 "read_chunks end=====+++++++++++++++++++++++++{} {} {}\n",
126                 dt.timestamp_nanos(),
127                 f(&self.chunk_read_state),
128                 chunks.len()
129             );
130         }
131 
132         if chunks.len() > 0 {
133             return Ok(UnpackResult::Chunks(chunks));
134         } else {
135             return Err(UnpackError {
136                 value: UnpackErrorValue::EmptyChunks,
137             });
138         }
139     }
140 
141     /******************************************************************************
142      * 5.3.1 Chunk Format
143      * Each chunk consists of a header and data. The header itself has three parts:
144      * +--------------+----------------+--------------------+--------------+
145      * | Basic Header | Message Header | Extended Timestamp | Chunk Data |
146      * +--------------+----------------+--------------------+--------------+
147      * |<------------------- Chunk Header ----------------->|
148      ******************************************************************************/
149     pub fn read_chunk(&mut self) -> Result<UnpackResult, UnpackError> {
150         let mut result: UnpackResult = UnpackResult::Empty;
151 
152         let dt: DateTime<Local> = Local::now();
153         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
154             print!("\n");
155             print!("\n");
156             print!(
157                 "read_chunk begin=====+++++++++++++++++++++++++{} {} {}\n",
158                 dt.timestamp_nanos(),
159                 f(&self.chunk_read_state),
160                 self.chunk_index
161             );
162         }
163         self.chunk_index = self.chunk_index + 1;
164 
165         loop {
166             result = match self.chunk_read_state {
167                 ChunkReadState::ReadBasicHeader => self.read_basic_header()?,
168                 ChunkReadState::ReadMessageHeader => self.read_message_header()?,
169                 ChunkReadState::ReadExtendedTimestamp => self.read_extended_timestamp()?,
170                 ChunkReadState::ReadMessagePayload => self.read_message_payload()?,
171                 ChunkReadState::Finish => {
172                     self.chunk_read_state = ChunkReadState::ReadBasicHeader;
173                     break;
174                 }
175             };
176         }
177         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
178             print!(
179                 "read_chunk end  =====+++++++++++++++++++++++++{}\n",
180                 dt.timestamp_nanos()
181             );
182         }
183         return Ok(result);
184 
185         // Ok(UnpackResult::Success)
186     }
187 
188     /******************************************************************
189      * 5.3.1.1. Chunk Basic Header
190      * The Chunk Basic Header encodes the chunk stream ID and the chunk
191      * type(represented by fmt field in the figure below). Chunk type
192      * determines the format of the encoded message header. Chunk Basic
193      * Header field may be 1, 2, or 3 bytes, depending on the chunk stream
194      * ID.
195      *
196      * The bits 0-5 (least significant) in the chunk basic header represent
197      * the chunk stream ID.
198      *
199      * Chunk stream IDs 2-63 can be encoded in the 1-byte version of this
200      * field.
201      *    0 1 2 3 4 5 6 7
202      *   +-+-+-+-+-+-+-+-+
203      *   |fmt|   cs id   |
204      *   +-+-+-+-+-+-+-+-+
205      *   Figure 6 Chunk basic header 1
206      *
207      * Chunk stream IDs 64-319 can be encoded in the 2-byte version of this
208      * field. ID is computed as (the second byte + 64).
209      *   0                   1
210      *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
211      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
212      *   |fmt|    0      | cs id - 64    |
213      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
214      *   Figure 7 Chunk basic header 2
215      *
216      * Chunk stream IDs 64-65599 can be encoded in the 3-byte version of
217      * this field. ID is computed as ((the third byte)*256 + the second byte
218      * + 64).
219      *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
220      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
221      *   |fmt|     1     |         cs id - 64            |
222      *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
223      *   Figure 8 Chunk basic header 3
224      *
225      * cs id: 6 bits
226      * fmt: 2 bits
227      * cs id - 64: 8 or 16 bits
228      *
229      * Chunk stream IDs with values 64-319 could be represented by both 2-
230      * byte version and 3-byte version of this field.
231      ***********************************************************************/
232 
233     pub fn read_basic_header(&mut self) -> Result<UnpackResult, UnpackError> {
234         let byte = self.reader.read_u8()?;
235 
236         let format_id = ((byte >> 6) & 0b00000011) as u8;
237         let mut csid = (byte & 0b00111111) as u32;
238 
239         match csid {
240             0 => {
241                 if self.reader.len() < 1 {
242                     return Ok(UnpackResult::NotEnoughBytes);
243                 }
244                 csid = 64;
245                 csid += self.reader.read_u8()? as u32;
246             }
247             1 => {
248                 if self.reader.len() < 1 {
249                     return Ok(UnpackResult::NotEnoughBytes);
250                 }
251                 csid = 64;
252                 csid += self.reader.read_u8()? as u32;
253                 csid += self.reader.read_u8()? as u32 * 256;
254             }
255             _ => {}
256         }
257 
258         //todo
259         if csid != self.current_chunk_info.basic_header.chunk_stream_id {
260             if let Some(header) = self.chunk_headers.get_mut(&csid) {
261                 self.current_chunk_info.basic_header = header.basic_header.clone();
262                 self.current_chunk_info.message_header = header.message_header.clone();
263             }
264         }
265 
266         self.current_chunk_info.basic_header.chunk_stream_id = csid;
267         self.current_chunk_info.basic_header.format = format_id;
268 
269         self.chunk_read_state = ChunkReadState::ReadMessageHeader;
270 
271         Ok(UnpackResult::ChunkBasicHeaderResult(ChunkBasicHeader::new(
272             format_id, csid,
273         )))
274     }
275 
276     fn current_message_header(&mut self) -> &mut ChunkMessageHeader {
277         &mut self.current_chunk_info.message_header
278     }
279 
280     pub fn read_message_header(&mut self) -> Result<UnpackResult, UnpackError> {
281         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
282             print!(
283                 "read_message_header left bytes length ======{}=======\n",
284                 self.reader.len()
285             );
286         }
287         match self.current_chunk_info.basic_header.format {
288             /*****************************************************************/
289             /*      5.3.1.2.1. Type 0                                        */
290             /*****************************************************************
291              0                   1                   2                   3
292              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
293             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
294             |                timestamp(3bytes)              |message length |
295             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
296             | message length (cont)(3bytes) |message type id| msg stream id |
297             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
298             |       message stream id (cont) (4bytes)       |
299             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
300             *****************************************************************/
301             0 => {
302                 loop {
303                     match self.msg_header_read_state {
304                         MessageHeaderReadState::ReadTimeStamp => {
305                             self.current_message_header().timestamp =
306                                 self.reader.read_u24::<BigEndian>()?;
307                             self.msg_header_read_state = MessageHeaderReadState::ReadMsgLength;
308                         }
309                         MessageHeaderReadState::ReadMsgLength => {
310                             self.current_message_header().msg_length =
311                                 self.reader.read_u24::<BigEndian>()?;
312 
313                             if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0)
314                             {
315                                 print!(
316                                     "msg_length format 0:======{}=======\n",
317                                     self.current_message_header().msg_length
318                                 );
319                             }
320                             self.msg_header_read_state = MessageHeaderReadState::ReadMsgTypeID;
321                         }
322                         MessageHeaderReadState::ReadMsgTypeID => {
323                             self.current_message_header().msg_type_id = self.reader.read_u8()?;
324 
325                             if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0)
326                             {
327                                 print!(
328                                     "msg_type_id format 0:======{}=======\n",
329                                     self.current_message_header().msg_type_id
330                                 );
331                             }
332                             self.msg_header_read_state = MessageHeaderReadState::ReadMsgStreamID;
333                         }
334                         MessageHeaderReadState::ReadMsgStreamID => {
335                             self.current_message_header().msg_streamd_id =
336                                 self.reader.read_u32::<LittleEndian>()?;
337                             self.msg_header_read_state = MessageHeaderReadState::ReadTimeStamp;
338                             break;
339                         }
340                     }
341                 }
342 
343                 if self.current_message_header().timestamp >= 0xFFFFFF {
344                     self.current_message_header().is_extended_timestamp = true;
345                 }
346             }
347             /*****************************************************************/
348             /*      5.3.1.2.2. Type 1                                        */
349             /*****************************************************************
350              0                   1                   2                   3
351              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
352             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
353             |                timestamp(3bytes)              |message length |
354             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
355             | message length (cont)(3bytes) |message type id|
356             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
357             *****************************************************************/
358             1 => {
359                 loop {
360                     match self.msg_header_read_state {
361                         MessageHeaderReadState::ReadTimeStamp => {
362                             self.current_message_header().timestamp_delta =
363                                 self.reader.read_u24::<BigEndian>()?;
364                             self.msg_header_read_state = MessageHeaderReadState::ReadMsgLength;
365                         }
366                         MessageHeaderReadState::ReadMsgLength => {
367                             self.current_message_header().msg_length =
368                                 self.reader.read_u24::<BigEndian>()?;
369 
370                             if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0)
371                             {
372                                 print!(
373                                     "msg_length format 1:======{}=======\n",
374                                     self.current_message_header().msg_length
375                                 );
376                             }
377                             self.msg_header_read_state = MessageHeaderReadState::ReadMsgTypeID;
378                         }
379                         MessageHeaderReadState::ReadMsgTypeID => {
380                             self.current_message_header().msg_type_id = self.reader.read_u8()?;
381 
382                             if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0)
383                             {
384                                 print!(
385                                     "msg_type_id format 1:======{}=======\n",
386                                     self.current_message_header().msg_type_id
387                                 );
388                             }
389                             self.msg_header_read_state = MessageHeaderReadState::ReadTimeStamp;
390                             break;
391                         }
392                         _ => {
393                             print!("error happend when read chunk message header\n");
394                             break;
395                         }
396                     }
397                 }
398 
399                 if self.current_message_header().timestamp_delta >= 0xFFFFFF {
400                     self.current_message_header().is_extended_timestamp = true;
401                 }
402             }
403             /************************************************/
404             /*      5.3.1.2.3. Type 2                       */
405             /************************************************
406              0                   1                   2
407              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
408             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409             |                timestamp(3bytes)              |
410             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411             ***************************************************/
412             2 => {
413                 if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
414                     print!(
415                         "msg_type_id format 2:======{}=======\n",
416                         self.current_message_header().msg_type_id
417                     );
418                 }
419                 self.current_message_header().timestamp_delta =
420                     self.reader.read_u24::<BigEndian>()?;
421 
422                 if self.current_message_header().timestamp_delta >= 0xFFFFFF {
423                     self.current_message_header().is_extended_timestamp = true;
424                 }
425             }
426 
427             _ => {}
428         }
429 
430         self.chunk_read_state = ChunkReadState::ReadExtendedTimestamp;
431 
432         Ok(UnpackResult::Success)
433     }
434 
435     pub fn read_extended_timestamp(&mut self) -> Result<UnpackResult, UnpackError> {
436         let mut extended_timestamp: u32 = 0;
437 
438         if self.current_message_header().is_extended_timestamp {
439             extended_timestamp = self.reader.read_u32::<BigEndian>()?;
440         }
441 
442         match self.current_chunk_info.basic_header.format {
443             0 => {
444                 if self.current_message_header().is_extended_timestamp {
445                     self.current_message_header().timestamp = extended_timestamp;
446                 }
447             }
448             1 => {
449                 if self.current_message_header().is_extended_timestamp {
450                     self.current_message_header().timestamp =
451                         self.current_message_header().timestamp - 0xFFFFFF + extended_timestamp;
452                 } else {
453                     self.current_message_header().timestamp +=
454                         self.current_message_header().timestamp_delta;
455                 }
456             }
457             2 => {
458                 if self.current_message_header().is_extended_timestamp {
459                     self.current_message_header().timestamp =
460                         self.current_message_header().timestamp - 0xFFFFFF + extended_timestamp;
461                 } else {
462                     self.current_message_header().timestamp +=
463                         self.current_message_header().timestamp_delta;
464                 }
465             }
466             //todo: 3 should also be processed
467             _ => {}
468         }
469 
470         self.chunk_read_state = ChunkReadState::ReadMessagePayload;
471 
472         Ok(UnpackResult::Success)
473     }
474 
475     pub fn read_message_payload(&mut self) -> Result<UnpackResult, UnpackError> {
476         let whole_msg_length = self.current_message_header().msg_length as usize;
477         let remaining_bytes = whole_msg_length - self.current_chunk_info.payload.len();
478 
479         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
480             print!(
481                 "whole:{} == remaining:{} \n",
482                 whole_msg_length, remaining_bytes
483             );
484         }
485 
486         let mut need_read_length = remaining_bytes;
487         if whole_msg_length > self.max_chunk_size {
488             need_read_length = min(remaining_bytes, self.max_chunk_size);
489         }
490 
491         let remaining_mut = self.current_chunk_info.payload.remaining_mut();
492         if need_read_length > remaining_mut {
493             let additional = need_read_length - remaining_mut;
494             self.current_chunk_info.payload.reserve(additional);
495         }
496         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
497             print!("buffer len:{}\n", self.reader.len());
498         }
499 
500         let payload_data = self.reader.read_bytes(need_read_length)?;
501         self.current_chunk_info
502             .payload
503             .extend_from_slice(&payload_data[..]);
504 
505         if config::DEBUG && ((config::DEBUG_INFO_TYPE & self.session_type) > 0) {
506             print!(
507                 "current msg payload len:{}\n",
508                 self.current_chunk_info.payload.len()
509             );
510         }
511 
512         if self.current_chunk_info.payload.len() == whole_msg_length {
513             self.chunk_read_state = ChunkReadState::Finish;
514             let chunk_info = self.current_chunk_info.clone();
515             self.current_chunk_info.payload.clear();
516 
517             let csid = self.current_chunk_info.basic_header.chunk_stream_id;
518 
519             //todo
520             if let Some(header) = self.chunk_headers.get_mut(&csid) {
521                 header.basic_header = self.current_chunk_info.basic_header.clone();
522                 header.message_header = self.current_chunk_info.message_header.clone();
523             } else {
524                 let chunk_header = ChunkHeader {
525                     basic_header: self.current_chunk_info.basic_header.clone(),
526                     message_header: self.current_chunk_info.message_header.clone(),
527                 };
528                 self.chunk_headers.insert(csid, chunk_header);
529             }
530 
531             // self.chunk_headers
532             //     .entry(self.current_chunk_info.basic_header.chunk_stream_id)
533             //     .or_insert(chunk_header);
534 
535             return Ok(UnpackResult::ChunkInfo(chunk_info));
536         }
537 
538         self.chunk_read_state = ChunkReadState::ReadBasicHeader;
539 
540         Ok(UnpackResult::Success)
541     }
542 }
543 
544 #[cfg(test)]
545 mod tests {
546 
547     use super::ChunkInfo;
548     use super::ChunkUnpacketizer;
549     use super::UnpackResult;
550     use bytes::BytesMut;
551 
552     #[test]
553     fn test_set_chunk_size() {
554         let mut unpacker = ChunkUnpacketizer::new();
555 
556         let data: [u8; 16] = [
557             //
558             02, //|format+csid|
559             00, 00, 00, //timestamp
560             00, 00, 04, //msg_length
561             01, //msg_type_id
562             00, 00, 00, 00, //msg_stream_id
563             00, 00, 10, 00, //body
564         ];
565 
566         unpacker.extend_data(&data[..]);
567 
568         let rv = unpacker.read_chunk();
569 
570         let mut body = BytesMut::new();
571         body.extend_from_slice(&[00, 00, 10, 00]);
572 
573         let expected = ChunkInfo::new(2, 0, 0, 4, 1, 0, body);
574 
575         assert_eq!(
576             rv.unwrap(),
577             UnpackResult::ChunkInfo(expected),
578             "not correct"
579         )
580     }
581 
582     #[test]
583     fn test_on_connect() {
584         // 0000   03 00 00 00 00 00 b1 14 00 00 00 00 02 00 07 63  ...............c
585         // 0010   6f 6e 6e 65 63 74 00 3f f0 00 00 00 00 00 00 03  onnect.?........
586         // 0020   00 03 61 70 70 02 00 06 68 61 72 6c 61 6e 00 04  ..app...harlan..
587         // 0030   74 79 70 65 02 00 0a 6e 6f 6e 70 72 69 76 61 74  type...nonprivat
588         // 0040   65 00 08 66 6c 61 73 68 56 65 72 02 00 1f 46 4d  e..flashVer...FM
589         // 0050   4c 45 2f 33 2e 30 20 28 63 6f 6d 70 61 74 69 62  LE/3.0 (compatib
590         // 0060   6c 65 3b 20 46 4d 53 63 2f 31 2e 30 29 00 06 73  le; FMSc/1.0)..s
591         // 0070   77 66 55 72 6c 02 00 1c 72 74 6d 70 3a 2f 2f 6c  wfUrl...rtmp://l
592         // 0080   6f 63 61 6c 68 6f 73 74 3a 31 39 33 35 2f 68 61  ocalhost:1935/ha
593         // 0090   72 6c 61 6e 00 05 74 63 55 72 6c 02 00 1c 72 74  rlan..tcUrl...rt
594         // 00a0   6d 70 3a 2f 2f 6c 6f 63 61 6c 68 6f 73 74 3a 31  mp://localhost:1
595         // 00b0   39 33 35 2f 68 61 72 6c 61 6e 00 00 09           935/harlan...
596         let data: [u8; 189] = [
597             3, //|format+csid|
598             0, 0, 0, //timestamp
599             0, 0, 177, //msg_length
600             20,  //msg_type_id 0x14
601             0, 0, 0, 0, //msg_stream_id
602             2, 0, 7, 99, 111, 110, 110, 101, 99, 116, 0, 63, 240, 0, 0, 0, 0, 0, 0, //body
603             3, 0, 3, 97, 112, 112, 2, 0, 6, 104, 97, 114, 108, 97, 110, 0, 4, 116, 121, 112, 101,
604             2, 0, 10, 110, 111, 110, 112, 114, 105, 118, 97, 116, 101, 0, 8, 102, 108, 97, 115,
605             104, 86, 101, 114, 2, 0, 31, 70, 77, 76, 69, 47, 51, 46, 48, 32, 40, 99, 111, 109, 112,
606             97, 116, 105, 98, 108, 101, 59, 32, 70, 77, 83, 99, 47, 49, 46, 48, 41, 0, 6, 115, 119,
607             102, 85, 114, 108, 2, 0, 28, 114, 116, 109, 112, 58, 47, 47, 108, 111, 99, 97, 108,
608             104, 111, 115, 116, 58, 49, 57, 51, 53, 47, 104, 97, 114, 108, 97, 110, 0, 5, 116, 99,
609             85, 114, 108, 2, 0, 28, 114, 116, 109, 112, 58, 47, 47, 108, 111, 99, 97, 108, 104,
610             111, 115, 116, 58, 49, 57, 51, 53, 47, 104, 97, 114, 108, 97, 110, 0, 0, 9,
611         ];
612 
613         let mut unpacker = ChunkUnpacketizer::new();
614         unpacker.extend_data(&data[..]);
615 
616         let rv = unpacker.read_chunk();
617 
618         let mut body = BytesMut::new();
619         body.extend_from_slice(&[
620             2, 0, 7, 99, 111, 110, 110, 101, 99, 116, 0, 63, 240, 0, 0, 0, 0, 0, 0, //body
621             3, 0, 3, 97, 112, 112, 2, 0, 6, 104, 97, 114, 108, 97, 110, 0, 4, 116, 121, 112, 101,
622             2, 0, 10, 110, 111, 110, 112, 114, 105, 118, 97, 116, 101, 0, 8, 102, 108, 97, 115,
623             104, 86, 101, 114, 2, 0, 31, 70, 77, 76, 69, 47, 51, 46, 48, 32, 40, 99, 111, 109, 112,
624             97, 116, 105, 98, 108, 101, 59, 32, 70, 77, 83, 99, 47, 49, 46, 48, 41, 0, 6, 115, 119,
625             102, 85, 114, 108, 2, 0, 28, 114, 116, 109, 112, 58, 47, 47, 108, 111, 99, 97, 108,
626             104, 111, 115, 116, 58, 49, 57, 51, 53, 47, 104, 97, 114, 108, 97, 110, 0, 5, 116, 99,
627             85, 114, 108, 2, 0, 28, 114, 116, 109, 112, 58, 47, 47, 108, 111, 99, 97, 108, 104,
628             111, 115, 116, 58, 49, 57, 51, 53, 47, 104, 97, 114, 108, 97, 110, 0, 0, 9,
629         ]);
630 
631         let expected = ChunkInfo::new(3, 0, 0, 177, 20, 0, body);
632 
633         assert_eq!(
634             rv.unwrap(),
635             UnpackResult::ChunkInfo(expected),
636             "not correct"
637         )
638     }
639 }
640