• 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 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