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 use std::io::{Cursor, Read}; 15 16 use ylong_http::body::{ChunkBodyDecoder, ChunkState, TextBodyDecoder}; 17 use ylong_http::headers::{HeaderName, HeaderValue, Headers}; 18 19 use super::Body; 20 use crate::error::{ErrorKind, HttpClientError}; 21 use crate::sync_impl::conn::StreamData; 22 23 /// `HttpBody` is the body part of the `Response` returned by `Client::request`. 24 /// `HttpBody` implements `Body` trait, so users can call related methods to get 25 /// body data. 26 /// 27 /// # Examples 28 /// 29 /// ```no_run 30 /// use ylong_http_client::sync_impl::{Body, Client, HttpBody}; 31 /// use ylong_http_client::{EmptyBody, Request}; 32 /// 33 /// let mut client = Client::new(); 34 /// 35 /// // `HttpBody` is the body part of `response`. 36 /// let mut response = client.request(Request::new(EmptyBody)).unwrap(); 37 /// 38 /// // Users can use `Body::data` to get body data. 39 /// let mut buf = [0u8; 1024]; 40 /// loop { 41 /// let size = response.body_mut().data(&mut buf).unwrap(); 42 /// if size == 0 { 43 /// break; 44 /// } 45 /// let _data = &buf[..size]; 46 /// // Deals with the data. 47 /// } 48 /// ``` 49 pub struct HttpBody { 50 kind: Kind, 51 } 52 53 type BoxStreamData = Box<dyn StreamData>; 54 55 impl HttpBody { empty() -> Self56 pub(crate) fn empty() -> Self { 57 Self { kind: Kind::Empty } 58 } 59 text(len: usize, pre: &[u8], io: BoxStreamData) -> Self60 pub(crate) fn text(len: usize, pre: &[u8], io: BoxStreamData) -> Self { 61 Self { 62 kind: Kind::Text(Text::new(len, pre, io)), 63 } 64 } 65 chunk(pre: &[u8], io: BoxStreamData, is_trailer: bool) -> Self66 pub(crate) fn chunk(pre: &[u8], io: BoxStreamData, is_trailer: bool) -> Self { 67 Self { 68 kind: Kind::Chunk(Chunk::new(pre, io, is_trailer)), 69 } 70 } 71 } 72 73 // TODO: `TextBodyDecoder` implementation and `ChunkBodyDecoder` implementation. 74 enum Kind { 75 Empty, 76 Text(Text), 77 Chunk(Chunk), 78 } 79 80 struct Text { 81 decoder: TextBodyDecoder, 82 pre: Option<Cursor<Vec<u8>>>, 83 io: Option<BoxStreamData>, 84 } 85 86 impl Text { new(len: usize, pre: &[u8], io: BoxStreamData) -> Self87 pub(crate) fn new(len: usize, pre: &[u8], io: BoxStreamData) -> Self { 88 Self { 89 decoder: TextBodyDecoder::new(len), 90 pre: (!pre.is_empty()).then_some(Cursor::new(pre.to_vec())), 91 io: Some(io), 92 } 93 } 94 } 95 96 impl Body for HttpBody { 97 type Error = HttpClientError; 98 data(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>99 fn data(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> { 100 if buf.is_empty() { 101 return Ok(0); 102 } 103 104 match self.kind { 105 Kind::Empty => Ok(0), 106 Kind::Text(ref mut text) => text.data(buf), 107 Kind::Chunk(ref mut chunk) => chunk.data(buf), 108 } 109 } 110 trailer(&mut self) -> Result<Option<Headers>, Self::Error>111 fn trailer(&mut self) -> Result<Option<Headers>, Self::Error> { 112 match self.kind { 113 Kind::Chunk(ref mut chunk) => chunk.get_trailer(), 114 _ => Ok(None), 115 } 116 } 117 } 118 119 impl Text { data(&mut self, buf: &mut [u8]) -> Result<usize, HttpClientError>120 fn data(&mut self, buf: &mut [u8]) -> Result<usize, HttpClientError> { 121 if buf.is_empty() { 122 return Ok(0); 123 } 124 125 let mut read = 0; 126 127 if let Some(pre) = self.pre.as_mut() { 128 // Here cursor read never failed. 129 let this_read = pre.read(buf).unwrap(); 130 if this_read == 0 { 131 self.pre = None; 132 } else { 133 read += this_read; 134 let (text, rem) = self.decoder.decode(&buf[..read]); 135 136 match (text.is_complete(), rem.is_empty()) { 137 (true, false) => { 138 if let Some(io) = self.io.take() { 139 io.shutdown(); 140 }; 141 return Err(HttpClientError::new_with_message( 142 ErrorKind::BodyDecode, 143 "Not Eof", 144 )); 145 } 146 (true, true) => { 147 self.io = None; 148 return Ok(read); 149 } 150 _ => {} 151 } 152 } 153 } 154 155 if !buf[read..].is_empty() { 156 if let Some(mut io) = self.io.take() { 157 match io.read(&mut buf[read..]) { 158 // Disconnected. 159 Ok(0) => { 160 io.shutdown(); 161 return Err(HttpClientError::new_with_message( 162 ErrorKind::BodyDecode, 163 "Response Body Incomplete", 164 )); 165 } 166 Ok(filled) => { 167 let (text, rem) = self.decoder.decode(&buf[read..read + filled]); 168 read += filled; 169 // Contains redundant `rem`, return error. 170 match (text.is_complete(), rem.is_empty()) { 171 (true, false) => { 172 io.shutdown(); 173 return Err(HttpClientError::new_with_message( 174 ErrorKind::BodyDecode, 175 "Not Eof", 176 )); 177 } 178 (true, true) => return Ok(read), 179 _ => {} 180 } 181 self.io = Some(io); 182 } 183 Err(e) => { 184 return Err(HttpClientError::new_with_cause( 185 ErrorKind::BodyTransfer, 186 Some(e), 187 )) 188 } 189 } 190 } 191 } 192 Ok(read) 193 } 194 } 195 196 struct Chunk { 197 decoder: ChunkBodyDecoder, 198 pre: Option<Cursor<Vec<u8>>>, 199 io: Option<BoxStreamData>, 200 trailer: Vec<u8>, 201 } 202 203 impl Chunk { new(pre: &[u8], io: BoxStreamData, is_trailer: bool) -> Self204 pub(crate) fn new(pre: &[u8], io: BoxStreamData, is_trailer: bool) -> Self { 205 Self { 206 decoder: ChunkBodyDecoder::new().contains_trailer(is_trailer), 207 pre: (!pre.is_empty()).then_some(Cursor::new(pre.to_vec())), 208 io: Some(io), 209 trailer: vec![], 210 } 211 } 212 } 213 214 impl Chunk { data(&mut self, buf: &mut [u8]) -> Result<usize, HttpClientError>215 fn data(&mut self, buf: &mut [u8]) -> Result<usize, HttpClientError> { 216 if buf.is_empty() { 217 return Ok(0); 218 } 219 220 let mut read = 0; 221 222 while let Some(pre) = self.pre.as_mut() { 223 // Here cursor read never failed. 224 let size = pre.read(&mut buf[read..]).unwrap(); 225 226 if size == 0 { 227 self.pre = None; 228 } 229 230 let (size, flag) = self.merge_chunks(&mut buf[read..read + size])?; 231 read += size; 232 233 if flag { 234 // Return if we find a 0-sized chunk. 235 self.io = None; 236 return Ok(read); 237 } else if read != 0 { 238 // Return if we get some data. 239 return Ok(read); 240 } 241 } 242 243 // Here `read` must be 0. 244 while let Some(mut io) = self.io.take() { 245 match io.read(&mut buf[read..]) { 246 Ok(filled) => { 247 if filled == 0 { 248 io.shutdown(); 249 return Err(HttpClientError::new_with_message( 250 ErrorKind::BodyDecode, 251 "Response Body Incomplete", 252 )); 253 } 254 let (size, flag) = self.merge_chunks(&mut buf[read..read + filled])?; 255 read += size; 256 if flag { 257 // Return if we find a 0-sized chunk. 258 // Return if we get some data. 259 return Ok(read); 260 } 261 self.io = Some(io); 262 if read != 0 { 263 return Ok(read); 264 } 265 } 266 Err(e) => { 267 return Err(HttpClientError::new_with_cause( 268 ErrorKind::BodyTransfer, 269 Some(e), 270 )) 271 } 272 } 273 } 274 Ok(read) 275 } 276 merge_chunks(&mut self, buf: &mut [u8]) -> Result<(usize, bool), HttpClientError>277 fn merge_chunks(&mut self, buf: &mut [u8]) -> Result<(usize, bool), HttpClientError> { 278 // Here we need to merge the chunks into one data block and return. 279 // The data arrangement in buf is as follows: 280 // 281 // data in buf: 282 // +------+------+------+------+------+------+------+ 283 // | data | len | data | len | ... | data | len | 284 // +------+------+------+------+------+------+------+ 285 // 286 // We need to merge these data blocks into one block: 287 // 288 // after merge: 289 // +---------------------------+ 290 // | data | 291 // +---------------------------+ 292 293 let (chunks, junk) = self 294 .decoder 295 .decode(buf) 296 .map_err(|e| HttpClientError::new_with_cause(ErrorKind::BodyDecode, Some(e)))?; 297 298 let mut finished = false; 299 let mut ptrs = Vec::new(); 300 for chunk in chunks.into_iter() { 301 if chunk.trailer().is_some() { 302 if chunk.state() == &ChunkState::Finish { 303 finished = true; 304 self.trailer.extend_from_slice(chunk.trailer().unwrap()); 305 self.trailer.extend_from_slice(b"\r\n"); 306 break; 307 } else if chunk.state() == &ChunkState::DataCrlf { 308 self.trailer.extend_from_slice(chunk.trailer().unwrap()); 309 self.trailer.extend_from_slice(b"\r\n"); 310 } else { 311 self.trailer.extend_from_slice(chunk.trailer().unwrap()); 312 } 313 } else { 314 if chunk.size() == 0 && chunk.state() != &ChunkState::MetaSize { 315 finished = true; 316 break; 317 } 318 let data = chunk.data(); 319 ptrs.push((data.as_ptr(), data.len())) 320 } 321 } 322 323 if finished && !junk.is_empty() { 324 return Err(HttpClientError::new_with_message( 325 ErrorKind::BodyDecode, 326 "Invalid Chunk Body", 327 )); 328 } 329 330 let start = buf.as_ptr(); 331 332 let mut idx = 0; 333 for (ptr, len) in ptrs.into_iter() { 334 let st = ptr as usize - start as usize; 335 let ed = st + len; 336 buf.copy_within(st..ed, idx); 337 idx += len; 338 } 339 Ok((idx, finished)) 340 } 341 get_trailer(&self) -> Result<Option<Headers>, HttpClientError>342 fn get_trailer(&self) -> Result<Option<Headers>, HttpClientError> { 343 if self.trailer.is_empty() { 344 return Err(HttpClientError::new_with_message( 345 ErrorKind::BodyDecode, 346 "No trailer received", 347 )); 348 } 349 350 let mut colon = 0; 351 let mut lf = 0; 352 let mut trailer_header_name = HeaderName::from_bytes(b"") 353 .map_err(|e| HttpClientError::new_with_cause(ErrorKind::BodyDecode, Some(e)))?; 354 let mut trailer_headers = Headers::new(); 355 for (i, b) in self.trailer.iter().enumerate() { 356 if *b == b' ' { 357 continue; 358 } 359 if *b == b':' { 360 colon = i; 361 if lf == 0 { 362 let trailer_name = &self.trailer[..colon]; 363 trailer_header_name = HeaderName::from_bytes(trailer_name).map_err(|e| { 364 HttpClientError::new_with_cause(ErrorKind::BodyDecode, Some(e)) 365 })?; 366 } else { 367 let trailer_name = &self.trailer[lf + 1..colon]; 368 trailer_header_name = HeaderName::from_bytes(trailer_name).map_err(|e| { 369 HttpClientError::new_with_cause(ErrorKind::BodyDecode, Some(e)) 370 })?; 371 } 372 continue; 373 } 374 375 if *b == b'\n' { 376 lf = i; 377 let trailer_value = &self.trailer[colon + 1..lf - 1]; 378 let trailer_header_value = HeaderValue::from_bytes(trailer_value) 379 .map_err(|e| HttpClientError::new_with_cause(ErrorKind::BodyDecode, Some(e)))?; 380 let _ = trailer_headers 381 .insert::<HeaderName, HeaderValue>( 382 trailer_header_name.clone(), 383 trailer_header_value.clone(), 384 ) 385 .map_err(|e| HttpClientError::new_with_cause(ErrorKind::BodyDecode, Some(e)))?; 386 } 387 } 388 Ok(Some(trailer_headers)) 389 } 390 } 391 392 #[cfg(test)] 393 mod ut_syn_http_body { 394 use ylong_http::body::ChunkBodyDecoder; 395 396 use crate::sync_impl::http_body::Chunk; 397 use crate::sync_impl::{Body, HttpBody}; 398 399 /// UT test cases for `HttpBody::empty`. 400 /// 401 /// # Brief 402 /// 1. Creates a `HttpBody` by calling `HttpBody::empty`. 403 /// 2. Calls `data` method. 404 /// 3. Checks if the result is correct. 405 #[test] ut_http_body_empty()406 fn ut_http_body_empty() { 407 let mut body = HttpBody::empty(); 408 let mut buf = []; 409 let data = body.data(&mut buf); 410 assert!(data.is_ok()); 411 assert_eq!(data.unwrap(), 0); 412 } 413 414 /// UT test cases for `Chunk::get_trailers`. 415 /// 416 /// # Brief 417 /// 1. Creates a `Chunk` and set `Trailer`. 418 /// 2. Calls `get_trailer` method. 419 /// 3. Checks if the result is correct. 420 #[test] ut_http_body_chunk()421 fn ut_http_body_chunk() { 422 let mut chunk = Chunk { 423 decoder: ChunkBodyDecoder::new().contains_trailer(true), 424 pre: None, 425 io: None, 426 trailer: vec![], 427 }; 428 let trailer_info = "Trailer1:value1\r\nTrailer2:value2\r\n"; 429 chunk.trailer.extend_from_slice(trailer_info.as_bytes()); 430 let data = chunk.get_trailer().unwrap().unwrap(); 431 let value1 = data.get("Trailer1"); 432 assert_eq!(value1.unwrap().to_str().unwrap(), "value1"); 433 let value2 = data.get("Trailer2"); 434 assert_eq!(value2.unwrap().to_str().unwrap(), "value2"); 435 } 436 } 437