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