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 ylong_http::request::method::Method;
15 use ylong_http::request::uri::{Scheme, Uri};
16 use ylong_http::request::Request;
17 use ylong_http::response::status::StatusCode;
18 use ylong_http::response::ResponsePart;
19 use ylong_http::version::Version;
20 
21 use crate::error::{ErrorKind, HttpClientError};
22 
23 pub(crate) struct RequestFormatter<'a, T> {
24     part: &'a mut Request<T>,
25 }
26 
27 impl<'a, T> RequestFormatter<'a, T> {
new(part: &'a mut Request<T>) -> Self28     pub(crate) fn new(part: &'a mut Request<T>) -> Self {
29         Self { part }
30     }
31 
format(&mut self) -> Result<(), HttpClientError>32     pub(crate) fn format(&mut self) -> Result<(), HttpClientError> {
33         if Version::HTTP1_0 == *self.part.version() && Method::CONNECT == *self.part.method() {
34             return Err(HttpClientError::from_str(
35                 ErrorKind::Request,
36                 "Unknown METHOD in HTTP/1.0",
37             ));
38         }
39         // TODO Formatting the uri in the request doesn't seem necessary.
40         let uri_formatter = UriFormatter::new();
41         uri_formatter.format(self.part.uri_mut())?;
42 
43         let host_value = format_host_value(self.part.uri())?;
44 
45         if self.part.headers_mut().get("Accept").is_none() {
46             let _ = self.part.headers_mut().insert("Accept", "*/*");
47         }
48 
49         let _ = self
50             .part
51             .headers_mut()
52             .insert("Host", host_value.as_bytes());
53 
54         Ok(())
55     }
56 }
57 
58 pub(crate) struct UriFormatter;
59 
60 impl UriFormatter {
new() -> Self61     pub(crate) fn new() -> Self {
62         Self
63     }
64 
format(&self, uri: &mut Uri) -> Result<(), HttpClientError>65     pub(crate) fn format(&self, uri: &mut Uri) -> Result<(), HttpClientError> {
66         let host = match uri.host() {
67             Some(host) => host.clone(),
68             None => return err_from_msg!(Request, "No host in url"),
69         };
70 
71         #[cfg(feature = "__tls")]
72         let mut scheme = Scheme::HTTPS;
73 
74         #[cfg(not(feature = "__tls"))]
75         let mut scheme = Scheme::HTTP;
76 
77         if let Some(req_scheme) = uri.scheme() {
78             scheme = req_scheme.clone()
79         };
80 
81         let port;
82 
83         if let Some(req_port) = uri.port().and_then(|port| port.as_u16().ok()) {
84             port = req_port;
85         } else {
86             match scheme {
87                 Scheme::HTTPS => port = 443,
88                 Scheme::HTTP => port = 80,
89             }
90         }
91 
92         let mut new_uri = Uri::builder();
93         new_uri = new_uri.scheme(scheme);
94         new_uri = new_uri.authority(format!("{}:{}", host.as_str(), port).as_bytes());
95 
96         match uri.path() {
97             None => new_uri = new_uri.path("/"),
98             Some(path) => {
99                 new_uri = new_uri.path(path.clone());
100             }
101         }
102 
103         if let Some(query) = uri.query() {
104             new_uri = new_uri.query(query.clone());
105         }
106 
107         *uri = new_uri
108             .build()
109             .map_err(|_| HttpClientError::from_str(ErrorKind::Request, "Normalize url failed"))?;
110 
111         Ok(())
112     }
113 }
114 
115 pub(crate) struct BodyLengthParser<'a> {
116     req_method: &'a Method,
117     part: &'a ResponsePart,
118 }
119 
120 impl<'a> BodyLengthParser<'a> {
new(req_method: &'a Method, part: &'a ResponsePart) -> Self121     pub(crate) fn new(req_method: &'a Method, part: &'a ResponsePart) -> Self {
122         Self { req_method, part }
123     }
124 
parse(&self) -> Result<BodyLength, HttpClientError>125     pub(crate) fn parse(&self) -> Result<BodyLength, HttpClientError> {
126         if self.part.status.is_informational()
127             || self.part.status == StatusCode::NO_CONTENT
128             || self.part.status == StatusCode::NOT_MODIFIED
129         {
130             return Ok(BodyLength::Empty);
131         }
132 
133         if (self.req_method == &Method::CONNECT && self.part.status.is_successful())
134             || self.req_method == &Method::HEAD
135         {
136             return Ok(BodyLength::Empty);
137         }
138 
139         #[cfg(feature = "http1_1")]
140         {
141             let transfer_encoding = self.part.headers.get("Transfer-Encoding");
142 
143             if transfer_encoding.is_some() {
144                 if self.part.version == Version::HTTP1_0 {
145                     return err_from_msg!(Request, "Illegal Transfer-Encoding in HTTP/1.0");
146                 }
147                 let transfer_encoding_contains_chunk = transfer_encoding
148                     .and_then(|v| v.to_string().ok())
149                     .and_then(|str| str.find("chunked"))
150                     .is_some();
151 
152                 return if transfer_encoding_contains_chunk {
153                     Ok(BodyLength::Chunk)
154                 } else {
155                     Ok(BodyLength::UntilClose)
156                 };
157             }
158         }
159 
160         let content_length = self.part.headers.get("Content-Length");
161 
162         if content_length.is_some() {
163             let content_length_valid = content_length
164                 .and_then(|v| v.to_string().ok())
165                 .and_then(|s| s.parse::<u64>().ok());
166 
167             return match content_length_valid {
168                 // If `content-length` is 0, the io stream cannot be read,
169                 // otherwise it will get stuck.
170                 Some(0) => Ok(BodyLength::Empty),
171                 Some(len) => Ok(BodyLength::Length(len)),
172                 None => err_from_msg!(Request, "Invalid response content-length"),
173             };
174         }
175 
176         Ok(BodyLength::UntilClose)
177     }
178 }
179 #[derive(PartialEq, Debug)]
180 pub(crate) enum BodyLength {
181     #[cfg(feature = "http1_1")]
182     Chunk,
183     Length(u64),
184     Empty,
185     UntilClose,
186 }
187 
format_host_value(uri: &Uri) -> Result<String, HttpClientError>188 pub(crate) fn format_host_value(uri: &Uri) -> Result<String, HttpClientError> {
189     let host_value = match (uri.host(), uri.port()) {
190         (Some(host), Some(port)) => {
191             if port
192                 .as_u16()
193                 .map_err(|e| HttpClientError::from_error(ErrorKind::Request, e))?
194                 == uri.scheme().unwrap_or(&Scheme::HTTP).default_port()
195             {
196                 host.to_string()
197             } else {
198                 uri.authority().unwrap().to_str()
199             }
200         }
201         (Some(host), None) => host.to_string(),
202         (None, _) => {
203             return err_from_msg!(Request, "Request Uri lack host");
204         }
205     };
206     Ok(host_value)
207 }
208 
209 #[cfg(test)]
210 mod ut_normalizer {
211     use ylong_http::h1::ResponseDecoder;
212     use ylong_http::request::method::Method;
213     use ylong_http::request::uri::{Uri, UriBuilder};
214     use ylong_http::request::Request;
215 
216     use crate::normalizer::UriFormatter;
217     use crate::util::normalizer::{
218         format_host_value, BodyLength, BodyLengthParser, RequestFormatter,
219     };
220 
221     /// UT test cases for `UriFormatter::format`.
222     ///
223     /// # Brief
224     /// 1. Creates a `UriFormatter`.
225     /// 2. Calls `UriFormatter::format` with `Uri` to get the result.
226     /// 3. Checks if the uri port result is correct.
227     #[test]
ut_uri_format()228     fn ut_uri_format() {
229         let mut uri = UriBuilder::new()
230             .scheme("http")
231             .authority("example.com")
232             .path("/foo")
233             .query("a=1")
234             .build()
235             .unwrap();
236         let uni = UriFormatter::new();
237         let _ = uni.format(&mut uri);
238         assert_eq!(uri.port().unwrap().as_str(), "80");
239 
240         let mut uri = Uri::from_bytes(b"http://example.com").unwrap();
241         let uni = UriFormatter::new();
242         let _ = uni.format(&mut uri);
243         assert_eq!(uri.path().unwrap().as_str(), "/");
244     }
245 
246     /// UT test cases for `RequestFormatter::normalize`.
247     ///
248     /// # Brief
249     /// 1. Creates a `RequestFormatter`.
250     /// 2. Calls `UriFormatter::normalize` to get the result.
251     /// 3. Checks if the request's header result is correct.
252     #[test]
ut_request_format()253     fn ut_request_format() {
254         let mut request = Request::new("this is a body");
255         let request_uri = request.uri_mut();
256         *request_uri = Uri::from_bytes(b"http://example1.com").unwrap();
257         let mut formatter = RequestFormatter::new(&mut request);
258         let _ = formatter.format();
259         let (part, _) = request.into_parts();
260         let res = part.headers.get("Host").unwrap();
261         assert_eq!(res.to_string().unwrap().as_bytes(), b"example1.com");
262     }
263 
264     /// UT test cases for `BodyLengthParser::parse`.
265     ///
266     /// # Brief
267     /// 1. Creates a `BodyLengthParser`.
268     /// 2. Calls `BodyLengthParser::parse` to get the result.
269     /// 3. Checks if the BodyLength result is correct.
270     #[test]
ut_body_length_parser()271     fn ut_body_length_parser() {
272         let response_str = "HTTP/1.1 202 \r\nAge: \t 270646 \t \t\r\nLocation: \t example3.com:80 \t \t\r\nDate: \t Mon, 19 Dec 2022 01:46:59 GMT \t \t\r\nEtag:\t \"3147526947+gzip\" \t \t\r\n\r\n".as_bytes();
273         let mut decoder = ResponseDecoder::new();
274         let result = decoder.decode(response_str).unwrap().unwrap();
275         let method = Method::GET;
276         let body_len_parser = BodyLengthParser::new(&method, &result.0);
277         let res = body_len_parser.parse().unwrap();
278         assert_eq!(res, BodyLength::UntilClose);
279 
280         let response_str = "HTTP/1.1 202 \r\nTransfer-Encoding: \t chunked \t \t\r\nLocation: \t example3.com:80 \t \t\r\nDate: \t Mon, 19 Dec 2022 01:46:59 GMT \t \t\r\nEtag:\t \"3147526947+gzip\" \t \t\r\n\r\n".as_bytes();
281         let mut decoder = ResponseDecoder::new();
282         let result = decoder.decode(response_str).unwrap().unwrap();
283         let method = Method::GET;
284         let body_len_parser = BodyLengthParser::new(&method, &result.0);
285         let res = body_len_parser.parse().unwrap();
286         assert_eq!(res, BodyLength::Chunk);
287 
288         let response_str = "HTTP/1.1 202 \r\nContent-Length: \t 20 \t \t\r\nLocation: \t example3.com:80 \t \t\r\nDate: \t Mon, 19 Dec 2022 01:46:59 GMT \t \t\r\nEtag:\t \"3147526947+gzip\" \t \t\r\n\r\n".as_bytes();
289         let mut decoder = ResponseDecoder::new();
290         let result = decoder.decode(response_str).unwrap().unwrap();
291         let method = Method::GET;
292         let body_len_parser = BodyLengthParser::new(&method, &result.0);
293         let res = body_len_parser.parse().unwrap();
294         assert_eq!(res, BodyLength::Length(20));
295 
296         let response_str = "HTTP/1.0 202 \r\nTransfer-Encoding: \t chunked \t \t\r\nLocation: \t example3.com:80 \t \t\r\nDate: \t Mon, 19 Dec 2022 01:46:59 GMT \t \t\r\nEtag:\t \"3147526947+gzip\" \t \t\r\n\r\n".as_bytes();
297         let mut decoder = ResponseDecoder::new();
298         let result = decoder.decode(response_str).unwrap().unwrap();
299         let method = Method::GET;
300         let body_len_parser = BodyLengthParser::new(&method, &result.0);
301         let res = body_len_parser.parse();
302         assert!(res
303             .map_err(|e| {
304                 assert_eq!(
305                     format!("{e}"),
306                     "Request Error: Illegal Transfer-Encoding in HTTP/1.0"
307                 );
308                 e
309             })
310             .is_err());
311     }
312 
313     /// UT test cases for function `format_host_value`.
314     ///
315     /// # Brief
316     /// 1. Creates a uri by calling `Uri::from_bytes`.
317     /// 2. Calls `format_host_value` to get the formatted `Host Header` value.
318     /// 3. Checks whether the `Host Header` value is correct.
319     #[test]
ut_format_host_value()320     fn ut_format_host_value() {
321         let uri = Uri::from_bytes(b"https://www.example.com:80").expect("Uri parse failed");
322         assert_eq!(format_host_value(&uri).unwrap(), "www.example.com:80");
323         let uri = Uri::from_bytes(b"https://www.example.com:443").expect("Uri parse failed");
324         assert_eq!(format_host_value(&uri).unwrap(), "www.example.com");
325         let uri = Uri::from_bytes(b"http://www.example.com:80").expect("Uri parse failed");
326         assert_eq!(format_host_value(&uri).unwrap(), "www.example.com");
327         let uri = Uri::from_bytes(b"http://www.example.com:443").expect("Uri parse failed");
328         assert_eq!(format_host_value(&uri).unwrap(), "www.example.com:443");
329         let uri = Uri::from_bytes(b"www.example.com:443").expect("Uri parse failed");
330         assert_eq!(format_host_value(&uri).unwrap(), "www.example.com:443");
331         let uri = Uri::from_bytes(b"www.example.com:80").expect("Uri parse failed");
332         assert_eq!(format_host_value(&uri).unwrap(), "www.example.com");
333         let uri = Uri::from_bytes(b"www.example.com").expect("Uri parse failed");
334         assert_eq!(format_host_value(&uri).unwrap(), "www.example.com");
335     }
336 }
337