• 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 ylong_http::body::{ChunkBody, TextBody};
15 use ylong_http::response::Response;
16 
17 use super::{conn, Body, ConnPool, Connector, HttpBody, HttpConnector};
18 use crate::async_impl::timeout::TimeoutFuture;
19 use crate::util::normalizer::{RequestFormatter, UriFormatter};
20 use crate::util::proxy::Proxies;
21 use crate::util::redirect::TriggerKind;
22 use crate::util::{ClientConfig, ConnectorConfig, HttpConfig, HttpVersion, Redirect};
23 #[cfg(feature = "http2")]
24 use crate::H2Config;
25 use crate::{sleep, timeout, ErrorKind, HttpClientError, Proxy, Request, Timeout, Uri};
26 
27 /// HTTP asynchronous client implementation. Users can use `async_impl::Client`
28 /// to send `Request` asynchronously. `async_impl::Client` depends on a
29 /// [`async_impl::Connector`] that can be customized by the user.
30 ///
31 /// [`async_impl::Connector`]: Connector
32 ///
33 /// # Examples
34 ///
35 /// ```
36 /// use ylong_http_client::async_impl::Client;
37 /// use ylong_http_client::{EmptyBody, Request};
38 ///
39 /// async fn async_client() {
40 ///     // Creates a new `Client`.
41 ///     let client = Client::new();
42 ///
43 ///     // Creates a new `Request`.
44 ///     let request = Request::new(EmptyBody);
45 ///
46 ///     // Sends `Request` and wait for the `Response` to return asynchronously.
47 ///     let response = client.request(request).await.unwrap();
48 ///
49 ///     // Gets the content of `Response`.
50 ///     let status = response.status();
51 /// }
52 /// ```
53 pub struct Client<C: Connector> {
54     inner: ConnPool<C, C::Stream>,
55     client_config: ClientConfig,
56     http_config: HttpConfig,
57 }
58 
59 impl Client<HttpConnector> {
60     /// Creates a new, default `AsyncClient`, which uses
61     /// [`async_impl::HttpConnector`].
62     ///
63     /// [`async_impl::HttpConnector`]: HttpConnector
64     ///
65     /// # Examples
66     ///
67     /// ```
68     /// use ylong_http_client::async_impl::Client;
69     ///
70     /// let client = Client::new();
71     /// ```
new() -> Self72     pub fn new() -> Self {
73         Self::with_connector(HttpConnector::default())
74     }
75 
76     /// Creates a new, default [`async_impl::ClientBuilder`].
77     ///
78     /// [`async_impl::ClientBuilder`]: ClientBuilder
79     ///
80     /// # Examples
81     ///
82     /// ```
83     /// use ylong_http_client::async_impl::Client;
84     ///
85     /// let builder = Client::builder();
86     /// ```
builder() -> ClientBuilder87     pub fn builder() -> ClientBuilder {
88         ClientBuilder::new()
89     }
90 }
91 
92 impl<C: Connector> Client<C> {
93     /// Creates a new, default `AsyncClient` with a given connector.
with_connector(connector: C) -> Self94     pub fn with_connector(connector: C) -> Self {
95         let http_config = HttpConfig::default();
96         Self {
97             inner: ConnPool::new(http_config.clone(), connector),
98             client_config: ClientConfig::default(),
99             http_config,
100         }
101     }
102 
103     /// Sends HTTP `Request` asynchronously.
104     ///
105     /// # Examples
106     ///
107     /// ```
108     /// use ylong_http_client::async_impl::Client;
109     /// use ylong_http_client::{EmptyBody, Request};
110     ///
111     /// async fn async_client() {
112     ///     let client = Client::new();
113     ///     let response = client.request(Request::new(EmptyBody)).await;
114     /// }
115     /// ```
116     // TODO: change result to `Response<HttpBody>` later.
request<T: Body>( &self, request: Request<T>, ) -> Result<super::Response, HttpClientError>117     pub async fn request<T: Body>(
118         &self,
119         request: Request<T>,
120     ) -> Result<super::Response, HttpClientError> {
121         let (part, body) = request.into_parts();
122 
123         let content_length = part
124             .headers
125             .get("Content-Length")
126             .and_then(|v| v.to_str().ok())
127             .and_then(|v| v.parse::<u64>().ok())
128             .is_some();
129 
130         let transfer_encoding = part
131             .headers
132             .get("Transfer-Encoding")
133             .and_then(|v| v.to_str().ok())
134             .map(|v| v.contains("chunked"))
135             .unwrap_or(false);
136 
137         let response = match (content_length, transfer_encoding) {
138             (_, true) => {
139                 let request = Request::from_raw_parts(part, ChunkBody::from_async_body(body));
140                 self.retry_send_request(request).await
141             }
142             (true, false) => {
143                 let request = Request::from_raw_parts(part, TextBody::from_async_body(body));
144                 self.retry_send_request(request).await
145             }
146             (false, false) => {
147                 let request = Request::from_raw_parts(part, body);
148                 self.retry_send_request(request).await
149             }
150         };
151         response.map(super::Response::new)
152     }
153 
retry_send_request<T: Body>( &self, mut request: Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>154     async fn retry_send_request<T: Body>(
155         &self,
156         mut request: Request<T>,
157     ) -> Result<Response<HttpBody>, HttpClientError> {
158         let mut retries = self.client_config.retry.times().unwrap_or(0);
159         loop {
160             let response = self.send_request_retryable(&mut request).await;
161             if response.is_ok() || retries == 0 {
162                 return response;
163             }
164             retries -= 1;
165         }
166     }
167 
send_request_retryable<T: Body>( &self, request: &mut Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>168     async fn send_request_retryable<T: Body>(
169         &self,
170         request: &mut Request<T>,
171     ) -> Result<Response<HttpBody>, HttpClientError> {
172         let response = self
173             .send_request_with_uri(request.uri().clone(), request)
174             .await?;
175         self.redirect_request(response, request).await
176     }
177 
redirect_request<T: Body>( &self, mut response: Response<HttpBody>, request: &mut Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>178     async fn redirect_request<T: Body>(
179         &self,
180         mut response: Response<HttpBody>,
181         request: &mut Request<T>,
182     ) -> Result<Response<HttpBody>, HttpClientError> {
183         let mut redirected_list = vec![];
184         let mut dst_uri = Uri::default();
185         loop {
186             if Redirect::is_redirect(response.status().clone(), request) {
187                 redirected_list.push(request.uri().clone());
188                 let trigger = Redirect::get_redirect(
189                     &mut dst_uri,
190                     &self.client_config.redirect,
191                     &redirected_list,
192                     &response,
193                     request,
194                 )?;
195 
196                 UriFormatter::new().format(&mut dst_uri)?;
197                 let _ = request
198                     .headers_mut()
199                     .insert("Host", dst_uri.authority().unwrap().to_string().as_bytes());
200                 match trigger {
201                     TriggerKind::NextLink => {
202                         response = self.send_request_with_uri(dst_uri.clone(), request).await?;
203                         continue;
204                     }
205                     TriggerKind::Stop => {
206                         return Ok(response);
207                     }
208                 }
209             } else {
210                 return Ok(response);
211             }
212         }
213     }
214 
send_request_with_uri<T: Body>( &self, mut uri: Uri, request: &mut Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>215     async fn send_request_with_uri<T: Body>(
216         &self,
217         mut uri: Uri,
218         request: &mut Request<T>,
219     ) -> Result<Response<HttpBody>, HttpClientError> {
220         UriFormatter::new().format(&mut uri)?;
221         RequestFormatter::new(request).normalize()?;
222 
223         match self.http_config.version {
224             #[cfg(feature = "http2")]
225             HttpVersion::Http2PriorKnowledge => self.http2_request(uri, request).await,
226             HttpVersion::Http11 => {
227                 let conn = if let Some(dur) = self.client_config.connect_timeout.inner() {
228                     match timeout(dur, self.inner.connect_to(uri)).await {
229                         Err(_elapsed) => {
230                             return Err(HttpClientError::new_with_message(
231                                 ErrorKind::Timeout,
232                                 "Connect timeout",
233                             ))
234                         }
235                         Ok(Ok(conn)) => conn,
236                         Ok(Err(e)) => return Err(e),
237                     }
238                 } else {
239                     self.inner.connect_to(uri).await?
240                 };
241 
242                 let mut retryable = Retryable::default();
243                 if let Some(timeout) = self.client_config.request_timeout.inner() {
244                     TimeoutFuture {
245                         timeout: Some(Box::pin(sleep(timeout))),
246                         future: Box::pin(conn::request(conn, request, &mut retryable)),
247                     }
248                     .await
249                 } else {
250                     conn::request(conn, request, &mut retryable).await
251                 }
252             }
253         }
254     }
255 
256     #[cfg(feature = "http2")]
http2_request<T: Body>( &self, uri: Uri, request: &mut Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>257     async fn http2_request<T: Body>(
258         &self,
259         uri: Uri,
260         request: &mut Request<T>,
261     ) -> Result<Response<HttpBody>, HttpClientError> {
262         let mut retryable = Retryable::default();
263 
264         const RETRY: usize = 1;
265         let mut times = 0;
266         loop {
267             retryable.set_retry(false);
268             let conn = self.inner.connect_to(uri.clone()).await?;
269             let response = conn::request(conn, request, &mut retryable).await;
270             if retryable.retry() && times < RETRY {
271                 times += 1;
272                 continue;
273             }
274             return response;
275         }
276     }
277 }
278 
279 impl Default for Client<HttpConnector> {
default() -> Self280     fn default() -> Self {
281         Self::new()
282     }
283 }
284 
285 #[derive(Default)]
286 pub(crate) struct Retryable {
287     #[cfg(feature = "http2")]
288     retry: bool,
289 }
290 
291 #[cfg(feature = "http2")]
292 impl Retryable {
set_retry(&mut self, retryable: bool)293     pub(crate) fn set_retry(&mut self, retryable: bool) {
294         self.retry = retryable
295     }
296 
retry(&self) -> bool297     pub(crate) fn retry(&self) -> bool {
298         self.retry
299     }
300 }
301 
302 /// A builder which is used to construct `async_impl::Client`.
303 ///
304 /// # Examples
305 ///
306 /// ```
307 /// use ylong_http_client::async_impl::ClientBuilder;
308 ///
309 /// let client = ClientBuilder::new().build();
310 /// ```
311 pub struct ClientBuilder {
312     /// Options and flags that is related to `HTTP`.
313     http: HttpConfig,
314 
315     /// Options and flags that is related to `Client`.
316     client: ClientConfig,
317 
318     /// Options and flags that is related to `Proxy`.
319     proxies: Proxies,
320 
321     /// Options and flags that is related to `TLS`.
322     #[cfg(feature = "__tls")]
323     tls: crate::util::TlsConfigBuilder,
324 }
325 
326 impl ClientBuilder {
327     /// Creates a new, default `ClientBuilder`.
328     ///
329     /// # Examples
330     ///
331     /// ```
332     /// use ylong_http_client::async_impl::ClientBuilder;
333     ///
334     /// let builder = ClientBuilder::new();
335     /// ```
new() -> Self336     pub fn new() -> Self {
337         Self {
338             http: HttpConfig::default(),
339             client: ClientConfig::default(),
340             proxies: Proxies::default(),
341 
342             #[cfg(feature = "__tls")]
343             tls: crate::util::TlsConfig::builder(),
344         }
345     }
346 
347     /// Only use HTTP/1.
348     ///
349     /// # Examples
350     ///
351     /// ```
352     /// use ylong_http_client::async_impl::ClientBuilder;
353     ///
354     /// let builder = ClientBuilder::new().http1_only();
355     /// ```
http1_only(mut self) -> Self356     pub fn http1_only(mut self) -> Self {
357         self.http.version = HttpVersion::Http11;
358         self
359     }
360 
361     /// Only use HTTP/2.
362     ///
363     /// # Examples
364     ///
365     /// ```
366     /// use ylong_http_client::async_impl::ClientBuilder;
367     ///
368     /// let builder = ClientBuilder::new().http2_prior_knowledge();
369     /// ```
370     #[cfg(feature = "http2")]
http2_prior_knowledge(mut self) -> Self371     pub fn http2_prior_knowledge(mut self) -> Self {
372         self.http.version = HttpVersion::Http2PriorKnowledge;
373         self
374     }
375 
376     /// HTTP/2 settings.
377     ///
378     /// # Examples
379     ///
380     /// ```
381     /// use ylong_http_client::async_impl::ClientBuilder;
382     /// use ylong_http_client::H2Config;
383     ///
384     /// let builder = ClientBuilder::new().http2_settings(H2Config::default());
385     /// ```
386     #[cfg(feature = "http2")]
http2_settings(mut self, config: H2Config) -> Self387     pub fn http2_settings(mut self, config: H2Config) -> Self {
388         self.http.http2_config = config;
389         self
390     }
391 
392     /// Enables a request timeout.
393     ///
394     /// The timeout is applied from when the request starts connection util the
395     /// response body has finished.
396     ///
397     /// # Examples
398     ///
399     /// ```
400     /// use ylong_http_client::async_impl::ClientBuilder;
401     /// use ylong_http_client::Timeout;
402     ///
403     /// let builder = ClientBuilder::new().request_timeout(Timeout::none());
404     /// ```
request_timeout(mut self, timeout: Timeout) -> Self405     pub fn request_timeout(mut self, timeout: Timeout) -> Self {
406         self.client.request_timeout = timeout;
407         self
408     }
409 
410     /// Sets a timeout for only the connect phase of `Client`.
411     ///
412     /// Default is `Timeout::none()`.
413     ///
414     /// # Examples
415     ///
416     /// ```
417     /// use ylong_http_client::async_impl::ClientBuilder;
418     /// use ylong_http_client::Timeout;
419     ///
420     /// let builder = ClientBuilder::new().connect_timeout(Timeout::none());
421     /// ```
connect_timeout(mut self, timeout: Timeout) -> Self422     pub fn connect_timeout(mut self, timeout: Timeout) -> Self {
423         self.client.connect_timeout = timeout;
424         self
425     }
426 
427     /// Sets a `Redirect` for this client.
428     ///
429     /// Default will follow redirects up to a maximum of 10.
430     ///
431     /// # Examples
432     ///
433     /// ```
434     /// use ylong_http_client::async_impl::ClientBuilder;
435     /// use ylong_http_client::Redirect;
436     ///
437     /// let builder = ClientBuilder::new().redirect(Redirect::none());
438     /// ```
redirect(mut self, redirect: Redirect) -> Self439     pub fn redirect(mut self, redirect: Redirect) -> Self {
440         self.client.redirect = redirect;
441         self
442     }
443 
444     /// Adds a `Proxy` to the list of proxies the `Client` will use.
445     ///
446     /// # Examples
447     ///
448     /// ```
449     /// # use ylong_http_client::async_impl::ClientBuilder;
450     /// # use ylong_http_client::{HttpClientError, Proxy};
451     ///
452     /// # fn add_proxy() -> Result<(), HttpClientError> {
453     /// let builder = ClientBuilder::new().proxy(Proxy::http("http://www.example.com").build()?);
454     /// # Ok(())
455     /// # }
456     /// ```
proxy(mut self, proxy: Proxy) -> Self457     pub fn proxy(mut self, proxy: Proxy) -> Self {
458         self.proxies.add_proxy(proxy.inner());
459         self
460     }
461 
462     /// Constructs a `Client` based on the given settings.
463     ///
464     /// # Examples
465     ///
466     /// ```
467     /// use ylong_http_client::async_impl::ClientBuilder;
468     ///
469     /// let client = ClientBuilder::new().build();
470     /// ```
build(self) -> Result<Client<HttpConnector>, HttpClientError>471     pub fn build(self) -> Result<Client<HttpConnector>, HttpClientError> {
472         let config = ConnectorConfig {
473             proxies: self.proxies,
474             #[cfg(feature = "__tls")]
475             tls: self.tls.build()?,
476         };
477 
478         let connector = HttpConnector::new(config);
479 
480         Ok(Client {
481             inner: ConnPool::new(self.http.clone(), connector),
482             client_config: self.client,
483             http_config: self.http,
484         })
485     }
486 }
487 
488 #[cfg(feature = "__tls")]
489 impl ClientBuilder {
490     /// Sets the maximum allowed TLS version for connections.
491     ///
492     /// By default there's no maximum.
493     ///
494     /// # Examples
495     ///
496     /// ```
497     /// use ylong_http_client::async_impl::ClientBuilder;
498     /// use ylong_http_client::TlsVersion;
499     ///
500     /// let builder = ClientBuilder::new().max_tls_version(TlsVersion::TLS_1_2);
501     /// ```
max_tls_version(mut self, version: crate::util::TlsVersion) -> Self502     pub fn max_tls_version(mut self, version: crate::util::TlsVersion) -> Self {
503         self.tls = self.tls.max_proto_version(version);
504         self
505     }
506 
507     /// Sets the minimum required TLS version for connections.
508     ///
509     /// By default the TLS backend's own default is used.
510     ///
511     /// # Examples
512     ///
513     /// ```
514     /// use ylong_http_client::async_impl::ClientBuilder;
515     /// use ylong_http_client::TlsVersion;
516     ///
517     /// let builder = ClientBuilder::new().min_tls_version(TlsVersion::TLS_1_2);
518     /// ```
min_tls_version(mut self, version: crate::util::TlsVersion) -> Self519     pub fn min_tls_version(mut self, version: crate::util::TlsVersion) -> Self {
520         self.tls = self.tls.min_proto_version(version);
521         self
522     }
523 
524     /// Adds a custom root certificate.
525     ///
526     /// This can be used to connect to a server that has a self-signed.
527     /// certificate for example.
528     ///
529     /// # Examples
530     ///
531     /// ```
532     /// use ylong_http_client::async_impl::ClientBuilder;
533     /// use ylong_http_client::Certificate;
534     ///
535     /// # fn set_cert(cert: Certificate) {
536     /// let builder = ClientBuilder::new().add_root_certificate(cert);
537     /// # }
538     /// ```
add_root_certificate(mut self, certs: crate::util::Certificate) -> Self539     pub fn add_root_certificate(mut self, certs: crate::util::Certificate) -> Self {
540         self.tls = self.tls.add_root_certificates(certs);
541         self
542     }
543 
544     /// Loads trusted root certificates from a file. The file should contain a
545     /// sequence of PEM-formatted CA certificates.
546     ///
547     /// # Examples
548     ///
549     /// ```
550     /// use ylong_http_client::async_impl::ClientBuilder;
551     ///
552     /// let builder = ClientBuilder::new().tls_ca_file("ca.crt");
553     /// ```
tls_ca_file(mut self, path: &str) -> Self554     pub fn tls_ca_file(mut self, path: &str) -> Self {
555         self.tls = self.tls.ca_file(path);
556         self
557     }
558 
559     /// Sets the list of supported ciphers for protocols before `TLSv1.3`.
560     ///
561     /// See [`ciphers`] for details on the format.
562     ///
563     /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html
564     ///
565     /// # Examples
566     ///
567     /// ```
568     /// use ylong_http_client::async_impl::ClientBuilder;
569     ///
570     /// let builder = ClientBuilder::new()
571     ///     .tls_cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
572     /// ```
tls_cipher_list(mut self, list: &str) -> Self573     pub fn tls_cipher_list(mut self, list: &str) -> Self {
574         self.tls = self.tls.cipher_list(list);
575         self
576     }
577 
578     /// Sets the list of supported ciphers for the `TLSv1.3` protocol.
579     ///
580     /// The format consists of TLSv1.3 cipher suite names separated by `:`
581     /// characters in order of preference.
582     ///
583     /// Requires `OpenSSL 1.1.1` or `LibreSSL 3.4.0` or newer.
584     ///
585     /// # Examples
586     ///
587     /// ```
588     /// use ylong_http_client::async_impl::ClientBuilder;
589     ///
590     /// let builder = ClientBuilder::new().tls_cipher_suites(
591     ///     "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK",
592     /// );
593     /// ```
tls_cipher_suites(mut self, list: &str) -> Self594     pub fn tls_cipher_suites(mut self, list: &str) -> Self {
595         self.tls = self.tls.cipher_suites(list);
596         self
597     }
598 
599     /// Controls the use of built-in system certificates during certificate
600     /// validation. Default to `true` -- uses built-in system certs.
601     ///
602     /// # Examples
603     ///
604     /// ```
605     /// use ylong_http_client::async_impl::ClientBuilder;
606     ///
607     /// let builder = ClientBuilder::new().tls_built_in_root_certs(false);
608     /// ```
tls_built_in_root_certs(mut self, is_use: bool) -> Self609     pub fn tls_built_in_root_certs(mut self, is_use: bool) -> Self {
610         self.tls = self.tls.build_in_root_certs(is_use);
611         self
612     }
613 
614     /// Controls the use of certificates verification.
615     ///
616     /// Defaults to `false` -- verify certificates.
617     ///
618     /// # Warning
619     ///
620     /// When sets `true`, any certificate for any site will be trusted for use.
621     ///
622     /// # Examples
623     ///
624     /// ```
625     /// use ylong_http_client::async_impl::ClientBuilder;
626     ///
627     /// let builder = ClientBuilder::new().danger_accept_invalid_certs(true);
628     /// ```
danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self629     pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self {
630         self.tls = self.tls.danger_accept_invalid_certs(is_invalid);
631         self
632     }
633 
634     /// Controls the use of hostname verification.
635     ///
636     /// Defaults to `false` -- verify hostname.
637     ///
638     /// # Warning
639     ///
640     /// When sets `true`, any valid certificate for any site will be trusted for
641     /// use from any other.
642     ///
643     /// # Examples
644     ///
645     /// ```
646     /// use ylong_http_client::async_impl::ClientBuilder;
647     ///
648     /// let builder = ClientBuilder::new().danger_accept_invalid_hostnames(true);
649     /// ```
danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self650     pub fn danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self {
651         self.tls = self.tls.danger_accept_invalid_hostnames(is_invalid);
652         self
653     }
654 
655     /// Controls the use of TLS server name indication.
656     ///
657     /// Defaults to `true` -- sets sni.
658     ///
659     /// # Examples
660     ///
661     /// ```
662     /// use ylong_http_client::async_impl::ClientBuilder;
663     ///
664     /// let builder = ClientBuilder::new().tls_sni(true);
665     /// ```
tls_sni(mut self, is_set_sni: bool) -> Self666     pub fn tls_sni(mut self, is_set_sni: bool) -> Self {
667         self.tls = self.tls.sni(is_set_sni);
668         self
669     }
670 }
671 
672 impl Default for ClientBuilder {
default() -> Self673     fn default() -> Self {
674         Self::new()
675     }
676 }
677 
678 #[cfg(test)]
679 mod ut_async_impl_client {
680     use crate::async_impl::Client;
681 
682     /// UT test cases for `Client::builder`.
683     ///
684     /// # Brief
685     /// 1. Creates a ClientBuilder by calling `Client::Builder`.
686     /// 2. Calls `http_config`, `client_config`, `build` on the builder
687     ///    respectively.
688     /// 3. Checks if the result is as expected.
689     #[test]
ut_client_builder()690     fn ut_client_builder() {
691         let builder = Client::builder().http1_only().build();
692         assert!(builder.is_ok())
693     }
694 
695     /// UT test cases for `ClientBuilder::default`.
696     ///
697     /// # Brief
698     /// 1. Creates a `ClientBuilder` by calling `ClientBuilder::default`.
699     /// 2. Calls `http_config`, `client_config`, `tls_config` and `build`
700     ///    respectively.
701     /// 3. Checks if the result is as expected.
702     #[cfg(feature = "__tls")]
703     #[test]
ut_client_builder_default()704     fn ut_client_builder_default() {
705         use crate::async_impl::ClientBuilder;
706         use crate::util::{Redirect, Timeout};
707 
708         let builder = ClientBuilder::default()
709             .redirect(Redirect::none())
710             .connect_timeout(Timeout::from_secs(9))
711             .build();
712         assert!(builder.is_ok())
713     }
714 }
715