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 crate::error::HttpClientError;
18 use crate::util::{proxy, redirect};
19 
20 /// Redirects settings of requests.
21 ///
22 /// # Example
23 ///
24 /// ```
25 /// use ylong_http_client::Redirect;
26 ///
27 /// // The default maximum number of redirects is 10.
28 /// let redirect = Redirect::default();
29 ///
30 /// // No redirect.
31 /// let no_redirect = Redirect::none();
32 ///
33 /// // Custom the number of redirects.
34 /// let max = Redirect::limited(10);
35 /// ```
36 #[derive(Clone, Debug, Eq, PartialEq, Default)]
37 pub struct Redirect(redirect::Redirect);
38 
39 impl Redirect {
40     /// Sets max number of redirects.
41     ///
42     /// # Examples
43     ///
44     /// ```
45     /// # use ylong_http_client::Redirect;
46     ///
47     /// let redirect = Redirect::limited(10);
48     /// ```
limited(max: usize) -> Self49     pub fn limited(max: usize) -> Self {
50         Self(redirect::Redirect::limited(max))
51     }
52 
53     /// Sets unlimited number of redirects.
54     ///
55     /// # Examples
56     ///
57     /// ```
58     /// # use ylong_http_client::Redirect;
59     ///
60     /// let redirect = Redirect::no_limit();
61     /// ```
no_limit() -> Self62     pub fn no_limit() -> Self {
63         Self(redirect::Redirect::limited(usize::MAX))
64     }
65 
66     /// Stops redirects.
67     ///
68     /// # Examples
69     ///
70     /// ```
71     /// # use ylong_http_client::Redirect;
72     ///
73     /// let redirect = Redirect::none();
74     /// ```
none() -> Self75     pub fn none() -> Self {
76         Self(redirect::Redirect::none())
77     }
78 
inner(&self) -> &redirect::Redirect79     pub(crate) fn inner(&self) -> &redirect::Redirect {
80         &self.0
81     }
82 }
83 
84 /// Retries settings of requests. The default value is `Retry::NEVER`.
85 ///
86 /// # Example
87 ///
88 /// ```
89 /// use ylong_http_client::Retry;
90 ///
91 /// // Never retry.
92 /// let never = Retry::none();
93 ///
94 /// // The maximum number of redirects is 3.
95 /// let max = Retry::max();
96 ///
97 /// // Custom the number of retries.
98 /// let custom = Retry::new(2).unwrap();
99 /// ```
100 #[derive(Clone, Debug, Eq, PartialEq)]
101 pub struct Retry(Option<usize>);
102 
103 impl Retry {
104     const MAX_RETRIES: usize = 3;
105 
106     /// Customizes the number of retries. Returns `Err` if `times` is greater
107     /// than 3.
108     ///
109     /// # Examples
110     ///
111     /// ```
112     /// use ylong_http_client::Retry;
113     ///
114     /// assert!(Retry::new(1).is_ok());
115     /// assert!(Retry::new(10).is_err());
116     /// ```
new(times: usize) -> Result<Self, HttpClientError>117     pub fn new(times: usize) -> Result<Self, HttpClientError> {
118         if times >= Self::MAX_RETRIES {
119             return err_from_msg!(Build, "Invalid params");
120         }
121         Ok(Self(Some(times)))
122     }
123 
124     /// Creates a `Retry` that indicates never retry.
125     ///
126     /// # Examples
127     ///
128     /// ```
129     /// use ylong_http_client::Retry;
130     ///
131     /// let retry = Retry::none();
132     /// ```
none() -> Self133     pub fn none() -> Self {
134         Self(None)
135     }
136 
137     /// Creates a `Retry` with a max retry times.
138     ///
139     /// The maximum number of redirects is 3.
140     ///
141     /// # Examples
142     ///
143     /// ```
144     /// use ylong_http_client::Retry;
145     ///
146     /// let retry = Retry::max();
147     /// ```
max() -> Self148     pub fn max() -> Self {
149         Self(Some(Self::MAX_RETRIES))
150     }
151 
152     /// Get the retry times, returns None if not set.
153     ///
154     /// # Examples
155     ///
156     /// ```
157     /// use ylong_http_client::Retry;
158     ///
159     /// assert!(Retry::default().times().is_none());
160     /// ```
times(&self) -> Option<usize>161     pub fn times(&self) -> Option<usize> {
162         self.0
163     }
164 }
165 
166 impl Default for Retry {
default() -> Self167     fn default() -> Self {
168         Self::none()
169     }
170 }
171 
172 /// Timeout settings.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use ylong_http_client::Timeout;
178 ///
179 /// let timeout = Timeout::none();
180 /// ```
181 #[derive(Clone, Debug, Eq, PartialEq)]
182 pub struct Timeout(Option<Duration>);
183 
184 impl Timeout {
185     /// Creates a `Timeout` without limiting the timeout.
186     ///
187     /// # Examples
188     ///
189     /// ```
190     /// use ylong_http_client::Timeout;
191     ///
192     /// let timeout = Timeout::none();
193     /// ```
none() -> Self194     pub fn none() -> Self {
195         Self(None)
196     }
197 
198     /// Creates a new `Timeout` from the specified number of whole seconds.
199     ///
200     /// # Examples
201     ///
202     /// ```
203     /// use ylong_http_client::Timeout;
204     ///
205     /// let timeout = Timeout::from_secs(9);
206     /// ```
from_secs(secs: u64) -> Self207     pub fn from_secs(secs: u64) -> Self {
208         Self(Some(Duration::from_secs(secs)))
209     }
210 
inner(&self) -> Option<Duration>211     pub(crate) fn inner(&self) -> Option<Duration> {
212         self.0
213     }
214 }
215 
216 impl Default for Timeout {
default() -> Self217     fn default() -> Self {
218         Self::none()
219     }
220 }
221 
222 /// Speed limit settings.
223 ///
224 /// # Examples
225 ///
226 /// ```
227 /// use ylong_http_client::SpeedLimit;
228 ///
229 /// let limit = SpeedLimit::new();
230 /// ```
231 pub struct SpeedLimit {
232     min: (u64, Duration),
233     max: u64,
234 }
235 
236 impl SpeedLimit {
237     /// Creates a new `SpeedLimit`.
238     ///
239     /// # Examples
240     ///
241     /// ```
242     /// use ylong_http_client::SpeedLimit;
243     ///
244     /// let limit = SpeedLimit::new();
245     /// ```
new() -> Self246     pub fn new() -> Self {
247         Self::none()
248     }
249 
250     /// Sets the minimum speed and the seconds for which the current speed is
251     /// allowed to be less than this minimum speed.
252     ///
253     /// The unit of speed is bytes per second, and the unit of duration is
254     /// seconds.
255     ///
256     /// The minimum speed cannot exceed the maximum speed that has been set. If
257     /// the set value exceeds the currently set maximum speed, the minimum speed
258     /// will be set to the current maximum speed.
259     ///
260     /// # Examples
261     ///
262     /// ```
263     /// use ylong_http_client::SpeedLimit;
264     ///
265     /// // Sets minimum speed is 1024B/s, the duration is 10s.
266     /// let limit = SpeedLimit::new().min_speed(1024, 10);
267     /// ```
min_speed(mut self, min: u64, secs: u64) -> Self268     pub fn min_speed(mut self, min: u64, secs: u64) -> Self {
269         self.min = (cmp::min(self.max, min), Duration::from_secs(secs));
270         self
271     }
272 
273     /// Sets the maximum speed.
274     ///
275     /// The unit of speed is bytes per second.
276     ///
277     /// The maximum speed cannot be lower than the minimum speed that has been
278     /// set. If the set value is lower than the currently set minimum speed, the
279     /// maximum speed will be set to the current minimum speed.
280     ///
281     /// # Examples
282     ///
283     /// ```
284     /// use ylong_http_client::SpeedLimit;
285     ///
286     /// let limit = SpeedLimit::new().max_speed(1024);
287     /// ```
max_speed(mut self, max: u64) -> Self288     pub fn max_speed(mut self, max: u64) -> Self {
289         self.max = cmp::max(self.min.0, max);
290         self
291     }
292 
293     /// Creates a `SpeedLimit` without limiting the speed.
294     ///
295     /// # Examples
296     ///
297     /// ```
298     /// use ylong_http_client::SpeedLimit;
299     ///
300     /// let limit = SpeedLimit::none();
301     /// ```
none() -> Self302     pub fn none() -> Self {
303         Self {
304             min: (0, Duration::MAX),
305             max: u64::MAX,
306         }
307     }
308 }
309 
310 impl Default for SpeedLimit {
default() -> Self311     fn default() -> Self {
312         Self::new()
313     }
314 }
315 
316 /// Proxy settings.
317 ///
318 /// `Proxy` has functions which is below:
319 ///
320 /// - replace origin uri by proxy uri to link proxy server.
321 /// - set username and password to login proxy server.
322 /// - set no proxy which can keep origin uri not to be replaced by proxy uri.
323 ///
324 /// # Examples
325 ///
326 /// ```
327 /// # use ylong_http_client::Proxy;
328 ///
329 /// // All http request will be intercepted by `https://www.example.com`,
330 /// // but https request will link to server directly.
331 /// let proxy = Proxy::http("http://www.example.com").build();
332 ///
333 /// // All https request will be intercepted by `http://www.example.com`,
334 /// // but http request will link to server directly.
335 /// let proxy = Proxy::https("http://www.example.com").build();
336 ///
337 /// // All https and http request will be intercepted by "http://www.example.com".
338 /// let proxy = Proxy::all("http://www.example.com").build();
339 /// ```
340 #[derive(Clone)]
341 pub struct Proxy(proxy::Proxy);
342 
343 impl Proxy {
344     /// Passes all HTTP and HTTPS to the proxy URL.
345     ///
346     /// # Examples
347     ///
348     /// ```
349     /// use ylong_http_client::Proxy;
350     ///
351     /// // All https and http request will be intercepted by `http://example.com`.
352     /// let builder = Proxy::all("http://example.com");
353     /// ```
all(addr: &str) -> ProxyBuilder354     pub fn all(addr: &str) -> ProxyBuilder {
355         ProxyBuilder {
356             inner: proxy::Proxy::all(addr),
357         }
358     }
359 
360     /// Passes HTTP to the proxy URL.
361     ///
362     /// # Examples
363     ///
364     /// ```
365     /// use ylong_http_client::Proxy;
366     ///
367     /// // All http request will be intercepted by https://example.com,
368     /// // but https request will link to server directly.
369     /// let proxy = Proxy::http("https://example.com");
370     /// ```
http(addr: &str) -> ProxyBuilder371     pub fn http(addr: &str) -> ProxyBuilder {
372         ProxyBuilder {
373             inner: proxy::Proxy::http(addr),
374         }
375     }
376 
377     /// Passes HTTPS to the proxy URL.
378     ///
379     /// # Examples
380     ///
381     /// ```
382     /// use ylong_http_client::Proxy;
383     ///
384     /// // All https request will be intercepted by http://example.com,
385     /// // but http request will link to server directly.
386     /// let proxy = Proxy::https("http://example.com");
387     /// ```
https(addr: &str) -> ProxyBuilder388     pub fn https(addr: &str) -> ProxyBuilder {
389         ProxyBuilder {
390             inner: proxy::Proxy::https(addr),
391         }
392     }
393 
inner(self) -> proxy::Proxy394     pub(crate) fn inner(self) -> proxy::Proxy {
395         self.0
396     }
397 }
398 
399 /// A builder that constructs a `Proxy`.
400 ///
401 /// # Examples
402 ///
403 /// ```
404 /// use ylong_http_client::Proxy;
405 ///
406 /// let proxy = Proxy::all("http://www.example.com")
407 ///     .basic_auth("Aladdin", "open sesame")
408 ///     .build();
409 /// ```
410 pub struct ProxyBuilder {
411     inner: Result<proxy::Proxy, HttpClientError>,
412 }
413 
414 impl ProxyBuilder {
415     /// Pass HTTPS to the proxy URL, but the https uri which is in the no proxy
416     /// list, will not pass the proxy URL.
417     ///
418     /// # Examples
419     ///
420     /// ```
421     /// use ylong_http_client::Proxy;
422     ///
423     /// let builder = Proxy::https("http://example.com").no_proxy("https://example2.com");
424     /// ```
no_proxy(mut self, no_proxy: &str) -> Self425     pub fn no_proxy(mut self, no_proxy: &str) -> Self {
426         self.inner = self.inner.map(|mut proxy| {
427             proxy.no_proxy(no_proxy);
428             proxy
429         });
430         self
431     }
432 
433     /// Pass HTTPS to the proxy URL, and set username and password which is
434     /// required by the proxy server.
435     ///
436     /// # Examples
437     ///
438     /// ```
439     /// use ylong_http_client::Proxy;
440     ///
441     /// let builder = Proxy::https("http://example.com").basic_auth("username", "password");
442     /// ```
basic_auth(mut self, username: &str, password: &str) -> Self443     pub fn basic_auth(mut self, username: &str, password: &str) -> Self {
444         self.inner = self.inner.map(|mut proxy| {
445             proxy.basic_auth(username, password);
446             proxy
447         });
448         self
449     }
450 
451     /// Constructs a `Proxy`.
452     ///
453     /// # Examples
454     ///
455     /// ```
456     /// use ylong_http_client::Proxy;
457     ///
458     /// let proxy = Proxy::all("http://proxy.example.com").build();
459     /// ```
build(self) -> Result<Proxy, HttpClientError>460     pub fn build(self) -> Result<Proxy, HttpClientError> {
461         Ok(Proxy(self.inner?))
462     }
463 }
464 
465 #[cfg(test)]
466 mod ut_settings {
467     use std::time::Duration;
468 
469     use ylong_http::request::uri::Uri;
470 
471     use crate::{Proxy, Redirect, Retry, SpeedLimit, Timeout};
472 
473     /// UT test cases for `Retry::new`.
474     ///
475     /// # Brief
476     /// 1. Creates a `Retry` by calling `Retry::new`.
477     /// 2. Checks if the results are correct.
478     #[test]
ut_retry_new()479     fn ut_retry_new() {
480         let retry = Retry::new(1);
481         assert!(retry.is_ok());
482         let retry = Retry::new(3);
483         assert!(retry.is_err());
484         let retry = Retry::new(10);
485         assert!(retry.is_err());
486     }
487 
488     /// UT test cases for `Redirect::default`.
489     ///
490     /// # Brief
491     /// 1. Creates a `Redirect` by calling `Redirect::default`.
492     /// 2. Creates a 10 Redirect.
493     /// 3. Checks if the results are correct.
494     #[test]
495     #[allow(clippy::redundant_clone)]
ut_redirect_clone()496     fn ut_redirect_clone() {
497         let redirect = Redirect::default();
498         let redirect_10 = Redirect::limited(10);
499         assert_eq!(redirect, redirect_10);
500         assert_eq!(redirect.clone(), redirect_10)
501     }
502 
503     /// UT test cases for `Retry::clone`.
504     ///
505     /// # Brief
506     /// 1. Creates a `Retry` by calling `Redirect::new`.
507     /// 2. Creates another `Retry` by `Redirect::clone`.
508     /// 3. Checks if the results are correct.
509     #[test]
ut_retry_clone()510     fn ut_retry_clone() {
511         let retry = Retry::new(1).unwrap();
512         assert_eq!(retry.clone(), retry)
513     }
514 
515     /// UT test cases for `Retry::default`.
516     ///
517     /// # Brief
518     /// 1. Creates a `Retry` by calling `Redirect::default`.
519     /// 2. Checks if the results are correct.
520     #[test]
ut_retry_default()521     fn ut_retry_default() {
522         let retry = Retry::default();
523         assert_eq!(retry, Retry::none())
524     }
525 
526     /// UT test cases for `Retry::max`.
527     ///
528     /// # Brief
529     /// 1. Creates a `Retry` by calling `Redirect::max`.
530     /// 2. Checks if the results are correct.
531     #[test]
ut_retry_max()532     fn ut_retry_max() {
533         let retry = Retry::max();
534         assert_eq!(retry.times(), Some(3))
535     }
536 
537     /// UT test cases for `Timeout::clone`.
538     ///
539     /// # Brief
540     /// 1. Creates a `Timeout` by calling `Timeout::from_secs`.
541     /// 2. Creates another `Timeout` by `Timeout::clone`.
542     /// 3. Checks if the results are correct.
543     #[test]
ut_timeout_clone()544     fn ut_timeout_clone() {
545         let timeout = Timeout::from_secs(5);
546         assert_eq!(timeout.clone(), timeout)
547     }
548 
549     /// UT test cases for `Timeout::default`.
550     ///
551     /// # Brief
552     /// 1. Creates a `Timeout` by calling `Timeout::default`.
553     /// 2. Checks if the results are correct.
554     #[test]
ut_timeout_default()555     fn ut_timeout_default() {
556         let timeout = Timeout::default();
557         assert_eq!(timeout, Timeout::none())
558     }
559 
560     /// UT test cases for `SpeedLimit::default`.
561     ///
562     /// # Brief
563     /// 1. Creates a `SpeedLimit` by calling `SpeedLimit::default`.
564     /// 2. Checks if the results are correct.
565     #[test]
ut_speed_limit_default()566     fn ut_speed_limit_default() {
567         let speed = SpeedLimit::new();
568         assert_eq!(speed.max, SpeedLimit::default().max)
569     }
570 
571     /// UT test cases for `SpeedLimit::min_speed`.
572     ///
573     /// # Brief
574     /// 1. Creates a `SpeedLimit` by calling `SpeedLimit::new`.
575     /// 2. Sets the max speed of `SpeedLimit`.
576     /// 3. Sets a min speed value that is greater than the max speed value.
577     /// 4. Checks if the results are correct.
578     #[test]
ut_speed_limit_min_speed()579     fn ut_speed_limit_min_speed() {
580         let speed = SpeedLimit::new();
581         let speed = speed.max_speed(1024);
582         let speed = speed.min_speed(2048, 12);
583         assert_eq!(speed.min, (1024, Duration::from_secs(12)))
584     }
585 
586     /// UT test cases for `Proxy::clone`.
587     ///
588     /// # Brief
589     /// 1. Creates a `Proxy` by calling `Proxy::all`.
590     /// 2. Creates another `Proxy` by `Timeout::clone`.
591     /// 3. Checks if the results are correct.
592     #[test]
ut_proxy_clone()593     fn ut_proxy_clone() {
594         let proxy = Proxy::all("http://127.0.0.1:6789")
595             .no_proxy("127.0.0.1")
596             .basic_auth("user", "password")
597             .build()
598             .unwrap();
599         let proxy_clone = proxy.clone();
600         let uri = Uri::from_bytes(b"http://127.0.0.1:3456").unwrap();
601         assert!(!proxy.inner().is_intercepted(&uri));
602         assert!(!proxy_clone.inner().is_intercepted(&uri));
603     }
604 
605     /// UT test cases for `Proxy::https`.
606     ///
607     /// # Brief
608     /// 1. Creates a `Proxy` by calling `Proxy::https`.
609     /// 2. Checks if the results are correct.
610     #[test]
ut_proxy_https()611     fn ut_proxy_https() {
612         let proxy = Proxy::https("http://127.0.0.1:6789").build().unwrap();
613         let uri = Uri::from_bytes(b"https://127.0.0.1:3456").unwrap();
614         assert!(proxy.inner().is_intercepted(&uri));
615     }
616 }
617