• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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