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 core::cmp; 15 use core::time::Duration; 16 17 use ylong_http::request::uri::Uri; 18 use ylong_http::request::Request; 19 use ylong_http::response::status::StatusCode; 20 use ylong_http::response::Response; 21 22 use crate::error::{ErrorKind, HttpClientError}; 23 use crate::util::redirect::{RedirectStrategy, TriggerKind}; 24 use crate::util::{proxy, redirect as redirect_util}; 25 26 /// Redirects settings of requests. 27 /// 28 /// # Example 29 /// 30 /// ``` 31 /// use ylong_http_client::Redirect; 32 /// 33 /// // The default maximum number of redirects is 10. 34 /// let redirect = Redirect::default(); 35 /// 36 /// // No redirect 37 /// let no_redirect = Redirect::none(); 38 /// 39 /// // Custom the number of redirects. 40 /// let max = Redirect::limited(10); 41 /// ``` 42 #[derive(Clone, Debug, Eq, PartialEq)] 43 pub struct Redirect(Option<RedirectStrategy>); 44 45 impl Redirect { 46 /// Gets the strategy of redirects. 47 /// 48 /// # Examples 49 /// 50 /// ``` 51 /// # use ylong_http_client::util::Redirect; 52 /// 53 /// # let redirect = Redirect::limited(10); 54 /// let strategy = redirect.redirect_strategy(); 55 /// 56 /// # assert!(strategy.is_some()); 57 /// ``` redirect_strategy(&self) -> Option<&redirect_util::RedirectStrategy>58 pub fn redirect_strategy(&self) -> Option<&redirect_util::RedirectStrategy> { 59 self.0.as_ref() 60 } 61 62 /// Sets max number of redirects. 63 /// 64 /// # Examples 65 /// 66 /// ``` 67 /// # use ylong_http_client::util::Redirect; 68 /// 69 /// let redirect = Redirect::limited(10); 70 /// ``` limited(max: usize) -> Self71 pub fn limited(max: usize) -> Self { 72 Self(Some(RedirectStrategy::limited(max))) 73 } 74 75 /// Sets unlimited number of redirects. 76 /// 77 /// # Examples 78 /// 79 /// ``` 80 /// # use ylong_http_client::util::Redirect; 81 /// 82 /// let redirect = Redirect::no_limit(); 83 /// ``` no_limit() -> Self84 pub fn no_limit() -> Self { 85 Self(Some(RedirectStrategy::limited(usize::MAX))) 86 } 87 88 /// Stops redirects. 89 /// 90 /// # Examples 91 /// 92 /// ``` 93 /// # use ylong_http_client::Redirect; 94 /// 95 /// let redirect = Redirect::none(); 96 /// ``` none() -> Self97 pub fn none() -> Self { 98 Self(Some(RedirectStrategy::none())) 99 } 100 get_redirect<T, K>( dst_uri: &mut Uri, redirect: &Redirect, redirect_list: &[Uri], response: &Response<K>, request: &mut Request<T>, ) -> Result<TriggerKind, HttpClientError>101 pub(crate) fn get_redirect<T, K>( 102 dst_uri: &mut Uri, 103 redirect: &Redirect, 104 redirect_list: &[Uri], 105 response: &Response<K>, 106 request: &mut Request<T>, 107 ) -> Result<TriggerKind, HttpClientError> { 108 redirect_util::Redirect::get_trigger_kind( 109 dst_uri, 110 redirect, 111 redirect_list, 112 response, 113 request, 114 ) 115 } 116 is_redirect<T>(status_code: StatusCode, request: &mut Request<T>) -> bool117 pub(crate) fn is_redirect<T>(status_code: StatusCode, request: &mut Request<T>) -> bool { 118 redirect_util::Redirect::check_redirect(status_code, request) 119 } 120 } 121 122 impl Default for Redirect { 123 // redirect default limit 10 times default() -> Self124 fn default() -> Self { 125 Self(Some(RedirectStrategy::default())) 126 } 127 } 128 129 /// Retries settings of requests. The default value is `Retry::NEVER`. 130 /// 131 /// # Example 132 /// 133 /// ``` 134 /// use ylong_http_client::Retry; 135 /// 136 /// // Never retry. 137 /// let never = Retry::none(); 138 /// 139 /// // The maximum number of redirects is 3. 140 /// let max = Retry::max(); 141 /// 142 /// // Custom the number of retries. 143 /// let custom = Retry::new(2).unwrap(); 144 /// ``` 145 #[derive(Clone, Debug, Eq, PartialEq)] 146 pub struct Retry(Option<usize>); 147 148 impl Retry { 149 const MAX_RETRIES: usize = 3; 150 151 /// Customizes the number of retries. Returns `Err` if `times` is greater 152 /// than 3. 153 /// 154 /// # Examples 155 /// 156 /// ``` 157 /// use ylong_http_client::util::Retry; 158 /// 159 /// assert!(Retry::new(1).is_ok()); 160 /// assert!(Retry::new(10).is_err()); 161 /// ``` new(times: usize) -> Result<Self, HttpClientError>162 pub fn new(times: usize) -> Result<Self, HttpClientError> { 163 if times >= Self::MAX_RETRIES { 164 return Err(HttpClientError::new_with_message( 165 ErrorKind::Build, 166 "Invalid Retry Times", 167 )); 168 } 169 Ok(Self(Some(times))) 170 } 171 172 /// Creates a `Retry` that indicates never retry. 173 /// 174 /// # Examples 175 /// 176 /// ``` 177 /// use ylong_http_client::Retry; 178 /// 179 /// let retry = Retry::none(); 180 /// ``` none() -> Self181 pub fn none() -> Self { 182 Self(None) 183 } 184 185 /// Creates a `Retry` with a max retry times. 186 /// 187 /// The maximum number of redirects is 3. 188 /// 189 /// # Examples 190 /// 191 /// ``` 192 /// use ylong_http_client::Retry; 193 /// 194 /// let retry = Retry::max(); 195 /// ``` max() -> Self196 pub fn max() -> Self { 197 Self(Some(Self::MAX_RETRIES)) 198 } 199 200 /// Get the retry times, returns None if not set. 201 /// 202 /// # Examples 203 /// 204 /// ``` 205 /// use ylong_http_client::util::Retry; 206 /// 207 /// assert!(Retry::default().times().is_none()); 208 /// ``` times(&self) -> Option<usize>209 pub fn times(&self) -> Option<usize> { 210 self.0 211 } 212 } 213 214 impl Default for Retry { default() -> Self215 fn default() -> Self { 216 Self::none() 217 } 218 } 219 220 /// Timeout settings. 221 /// 222 /// # Examples 223 /// 224 /// ``` 225 /// use ylong_http_client::Timeout; 226 /// 227 /// let timeout = Timeout::none(); 228 /// ``` 229 #[derive(Clone, Debug, Eq, PartialEq)] 230 pub struct Timeout(Option<Duration>); 231 232 impl Timeout { 233 /// Creates a `Timeout` without limiting the timeout. 234 /// 235 /// # Examples 236 /// 237 /// ``` 238 /// use ylong_http_client::Timeout; 239 /// 240 /// let timeout = Timeout::none(); 241 /// ``` none() -> Self242 pub fn none() -> Self { 243 Self(None) 244 } 245 246 /// Creates a new `Timeout` from the specified number of whole seconds. 247 /// 248 /// # Examples 249 /// 250 /// ``` 251 /// use ylong_http_client::Timeout; 252 /// 253 /// let timeout = Timeout::from_secs(9); 254 /// ``` from_secs(secs: u64) -> Self255 pub fn from_secs(secs: u64) -> Self { 256 Self(Some(Duration::from_secs(secs))) 257 } 258 inner(&self) -> Option<Duration>259 pub(crate) fn inner(&self) -> Option<Duration> { 260 self.0 261 } 262 } 263 264 impl Default for Timeout { default() -> Self265 fn default() -> Self { 266 Self::none() 267 } 268 } 269 270 /// Speed limit settings. 271 /// 272 /// # Examples 273 /// 274 /// ``` 275 /// use ylong_http_client::SpeedLimit; 276 /// 277 /// let limit = SpeedLimit::new(); 278 /// ``` 279 pub struct SpeedLimit { 280 min: (u64, Duration), 281 max: u64, 282 } 283 284 impl SpeedLimit { 285 /// Creates a new `SpeedLimit`. 286 /// 287 /// # Examples 288 /// 289 /// ``` 290 /// use ylong_http_client::SpeedLimit; 291 /// 292 /// let limit = SpeedLimit::new(); 293 /// ``` new() -> Self294 pub fn new() -> Self { 295 Self::none() 296 } 297 298 /// Sets the minimum speed and the seconds for which the current speed is 299 /// allowed to be less than this minimum speed. 300 /// 301 /// The unit of speed is bytes per second, and the unit of duration is 302 /// seconds. 303 /// 304 /// The minimum speed cannot exceed the maximum speed that has been set. If 305 /// the set value exceeds the currently set maximum speed, the minimum speed 306 /// will be set to the current maximum speed. 307 /// 308 /// # Examples 309 /// 310 /// ``` 311 /// use ylong_http_client::SpeedLimit; 312 /// 313 /// // Sets minimum speed is 1024B/s, the duration is 10s. 314 /// let limit = SpeedLimit::new().min_speed(1024, 10); 315 /// ``` min_speed(mut self, min: u64, secs: u64) -> Self316 pub fn min_speed(mut self, min: u64, secs: u64) -> Self { 317 self.min = (cmp::min(self.max, min), Duration::from_secs(secs)); 318 self 319 } 320 321 /// Sets the maximum speed. 322 /// 323 /// The unit of speed is bytes per second. 324 /// 325 /// The maximum speed cannot be lower than the minimum speed that has been 326 /// set. If the set value is lower than the currently set minimum speed, the 327 /// maximum speed will be set to the current minimum speed. 328 /// 329 /// # Examples 330 /// 331 /// ``` 332 /// use ylong_http_client::SpeedLimit; 333 /// 334 /// let limit = SpeedLimit::new().max_speed(1024); 335 /// ``` max_speed(mut self, max: u64) -> Self336 pub fn max_speed(mut self, max: u64) -> Self { 337 self.max = cmp::max(self.min.0, max); 338 self 339 } 340 341 /// Creates a `SpeedLimit` without limiting the speed. 342 /// 343 /// # Examples 344 /// 345 /// ``` 346 /// use ylong_http_client::SpeedLimit; 347 /// 348 /// let limit = SpeedLimit::none(); 349 /// ``` none() -> Self350 pub fn none() -> Self { 351 Self { 352 min: (0, Duration::MAX), 353 max: u64::MAX, 354 } 355 } 356 } 357 358 impl Default for SpeedLimit { default() -> Self359 fn default() -> Self { 360 Self::new() 361 } 362 } 363 364 /// Proxy settings. 365 /// 366 /// `Proxy` has functions which is below: 367 /// 368 /// - replace origin uri by proxy uri to link proxy server. 369 /// - set username and password to login proxy server. 370 /// - set no proxy which can keep origin uri not to be replaced by proxy uri. 371 /// 372 /// # Examples 373 /// 374 /// ``` 375 /// # use ylong_http_client::Proxy; 376 /// 377 /// // All http request will be intercepted by `https://www.example.com`, 378 /// // but https request will link to server directly. 379 /// let proxy = Proxy::http("http://www.example.com").build(); 380 /// 381 /// // All https request will be intercepted by `http://www.example.com`, 382 /// // but http request will link to server directly. 383 /// let proxy = Proxy::https("http://www.example.com").build(); 384 /// 385 /// // All https and http request will be intercepted by "http://www.example.com". 386 /// let proxy = Proxy::all("http://www.example.com").build(); 387 /// ``` 388 #[derive(Clone)] 389 pub struct Proxy(proxy::Proxy); 390 391 impl Proxy { 392 /// Passes all HTTP and HTTPS to the proxy URL. 393 /// 394 /// # Examples 395 /// 396 /// ``` 397 /// use ylong_http_client::Proxy; 398 /// 399 /// // All https and http request will be intercepted by `http://example.com`. 400 /// let builder = Proxy::all("http://example.com"); 401 /// ``` all(addr: &str) -> ProxyBuilder402 pub fn all(addr: &str) -> ProxyBuilder { 403 ProxyBuilder { 404 inner: proxy::Proxy::all(addr), 405 } 406 } 407 408 /// Passes HTTP to the proxy URL. 409 /// 410 /// # Examples 411 /// 412 /// ``` 413 /// use ylong_http_client::Proxy; 414 /// 415 /// // All http request will be intercepted by https://example.com, 416 /// // but https request will link to server directly. 417 /// let proxy = Proxy::http("https://example.com"); 418 /// ``` http(addr: &str) -> ProxyBuilder419 pub fn http(addr: &str) -> ProxyBuilder { 420 ProxyBuilder { 421 inner: proxy::Proxy::http(addr), 422 } 423 } 424 425 /// Passes HTTPS to the proxy URL. 426 /// 427 /// # Examples 428 /// 429 /// ``` 430 /// use ylong_http_client::Proxy; 431 /// 432 /// // All https request will be intercepted by http://example.com, 433 /// // but http request will link to server directly. 434 /// let proxy = Proxy::https("http://example.com"); 435 /// ``` https(addr: &str) -> ProxyBuilder436 pub fn https(addr: &str) -> ProxyBuilder { 437 ProxyBuilder { 438 inner: proxy::Proxy::https(addr), 439 } 440 } 441 inner(self) -> proxy::Proxy442 pub(crate) fn inner(self) -> proxy::Proxy { 443 self.0 444 } 445 } 446 447 /// A builder that constructs a `Proxy`. 448 /// 449 /// # Examples 450 /// 451 /// ``` 452 /// use ylong_http_client::Proxy; 453 /// 454 /// let proxy = Proxy::all("http://www.example.com") 455 /// .basic_auth("Aladdin", "open sesame") 456 /// .build(); 457 /// ``` 458 pub struct ProxyBuilder { 459 inner: Result<proxy::Proxy, HttpClientError>, 460 } 461 462 impl ProxyBuilder { 463 /// Pass HTTPS to the proxy URL, but the https uri which is in the no proxy 464 /// list, will not pass the proxy URL. 465 /// 466 /// # Examples 467 /// 468 /// ``` 469 /// use ylong_http_client::Proxy; 470 /// 471 /// let builder = Proxy::https("http://example.com").no_proxy("https://example2.com"); 472 /// ``` no_proxy(mut self, no_proxy: &str) -> Self473 pub fn no_proxy(mut self, no_proxy: &str) -> Self { 474 self.inner = self.inner.map(|mut proxy| { 475 proxy.no_proxy(no_proxy); 476 proxy 477 }); 478 self 479 } 480 481 /// Pass HTTPS to the proxy URL, and set username and password which is 482 /// required by the proxy server. 483 /// 484 /// # Examples 485 /// 486 /// ``` 487 /// use ylong_http_client::Proxy; 488 /// 489 /// let builder = Proxy::https("http://example.com").basic_auth("username", "password"); 490 /// ``` basic_auth(mut self, username: &str, password: &str) -> Self491 pub fn basic_auth(mut self, username: &str, password: &str) -> Self { 492 self.inner = self.inner.map(|mut proxy| { 493 proxy.basic_auth(username, password); 494 proxy 495 }); 496 self 497 } 498 499 /// Constructs a `Proxy`. 500 /// 501 /// # Examples 502 /// 503 /// ``` 504 /// use ylong_http_client::Proxy; 505 /// 506 /// let proxy = Proxy::all("http://proxy.example.com").build(); 507 /// ``` build(self) -> Result<Proxy, HttpClientError>508 pub fn build(self) -> Result<Proxy, HttpClientError> { 509 Ok(Proxy(self.inner?)) 510 } 511 } 512 513 #[cfg(test)] 514 mod ut_settings { 515 use ylong_http::h1::ResponseDecoder; 516 use ylong_http::request::uri::Uri; 517 use ylong_http::request::Request; 518 use ylong_http::response::status::StatusCode; 519 use ylong_http::response::Response; 520 521 use crate::error::HttpClientError; 522 use crate::util::redirect as redirect_util; 523 use crate::util::redirect::TriggerKind; 524 use crate::{Redirect, Retry}; 525 create_trigger( redirect: &Redirect, previous: &[Uri], ) -> Result<redirect_util::TriggerKind, HttpClientError>526 fn create_trigger( 527 redirect: &Redirect, 528 previous: &[Uri], 529 ) -> Result<redirect_util::TriggerKind, HttpClientError> { 530 let redirect_status = redirect_util::RedirectStatus::new(previous); 531 redirect.0.as_ref().unwrap().get_trigger(redirect_status) 532 } 533 /// UT test cases for `Redirect::is_redirect`. 534 /// 535 /// # Brief 536 /// 1. Creates a `request` by calling `request::new`. 537 /// 2. Uses `redirect::is_redirect` to check whether is redirected. 538 /// 3. Checks if the result is true. 539 #[test] ut_setting_is_redirect()540 fn ut_setting_is_redirect() { 541 let mut request = Request::new("this is a body"); 542 let code = StatusCode::MOVED_PERMANENTLY; 543 let res = Redirect::is_redirect(code, &mut request); 544 assert!(res); 545 } 546 /// UT test cases for `Redirect::get_redirect` error branch. 547 /// 548 /// # Brief 549 /// 1. Creates a `redirect` by calling `Redirect::default`. 550 /// 2. Uses `Redirect::get_redirect` to get redirected trigger kind. 551 /// 3. Checks if the results are error. 552 #[test] ut_setting_get_redirect_kind_err()553 fn ut_setting_get_redirect_kind_err() { 554 let response_str = "HTTP/1.1 304 \r\nAge: \t 270646 \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(); 555 let mut decoder = ResponseDecoder::new(); 556 let result = decoder.decode(response_str).unwrap().unwrap(); 557 let response = Response::from_raw_parts(result.0, result.1); 558 let mut request = Request::new("this is a body"); 559 let mut uri = Uri::default(); 560 let redirect = Redirect::default(); 561 let redirect_list: Vec<Uri> = vec![]; 562 let res = 563 Redirect::get_redirect(&mut uri, &redirect, &redirect_list, &response, &mut request); 564 assert!(res.is_err()); 565 } 566 567 /// UT test cases for `Redirect::default`. 568 /// 569 /// # Brief 570 /// 1. Creates a `Redirect` by calling `Redirect::default`. 571 /// 2. Uses `Redirect::create_trigger` to get redirected uri. 572 /// 3. Checks if the results are correct. 573 #[test] ut_setting_redirect_default()574 fn ut_setting_redirect_default() { 575 let redirect = Redirect::default(); 576 let next = Uri::from_bytes(b"http://example.com").unwrap(); 577 let previous = (0..9) 578 .map(|i| Uri::from_bytes(format!("http://example{i}.com").as_bytes()).unwrap()) 579 .collect::<Vec<_>>(); 580 581 let redirect_uri = match create_trigger(&redirect, &previous).unwrap() { 582 TriggerKind::NextLink => next.to_string(), 583 TriggerKind::Stop => previous.get(9).unwrap().to_string(), 584 }; 585 assert_eq!(redirect_uri, "http://example.com".to_string()); 586 } 587 588 /// UT test cases for `Redirect::max_limit`. 589 /// 590 /// # Brief 591 /// 1. Creates a `Redirect` by calling `Redirect::max_limit`. 592 /// 2. Sets redirect times which is over max limitation times. 593 /// 2. Uses `Redirect::create_trigger` to get redirected uri. 594 /// 3. Checks if the results are err. 595 #[test] ut_setting_redirect_over_redirect_max()596 fn ut_setting_redirect_over_redirect_max() { 597 let redirect = Redirect::limited(10); 598 let previous = (0..10) 599 .map(|i| Uri::from_bytes(format!("http://example{i}.com").as_bytes()).unwrap()) 600 .collect::<Vec<_>>(); 601 602 if let Ok(other) = create_trigger(&redirect, &previous) { 603 panic!("unexpected {:?}", other); 604 }; 605 } 606 607 /// UT test cases for `Redirect::no_redirect`. 608 /// 609 /// # Brief 610 /// 1. Creates a `Redirect` by calling `Redirect::no_redirect`. 611 /// 2. Uses `Redirect::create_trigger` but get origin uri. 612 /// 3. Checks if the results are correct. 613 #[test] ut_setting_no_redirect()614 fn ut_setting_no_redirect() { 615 let redirect = Redirect::none(); 616 let next = Uri::from_bytes(b"http://example.com").unwrap(); 617 let previous = (0..1) 618 .map(|i| Uri::from_bytes(format!("http://example{i}.com").as_bytes()).unwrap()) 619 .collect::<Vec<_>>(); 620 621 let redirect_uri = match create_trigger(&redirect, &previous).unwrap() { 622 TriggerKind::NextLink => next.to_string(), 623 TriggerKind::Stop => previous.get(0).unwrap().to_string(), 624 }; 625 assert_eq!(redirect_uri, "http://example0.com".to_string()); 626 } 627 628 /// UT test cases for `Retry::new`. 629 /// 630 /// # Brief 631 /// 1. Creates a `Retry` by calling `Retry::new`. 632 /// 2. Checks if the results are correct. 633 #[test] ut_retry_new()634 fn ut_retry_new() { 635 let retry = Retry::new(1); 636 assert!(retry.is_ok()); 637 let retry = Retry::new(3); 638 assert!(retry.is_err()); 639 let retry = Retry::new(10); 640 assert!(retry.is_err()); 641 } 642 } 643