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::uri::Uri;
15 
16 use super::{Body, Connector, HttpBody, HttpConnector, Request, Response};
17 use crate::error::HttpClientError;
18 use crate::sync_impl::conn;
19 use crate::sync_impl::pool::ConnPool;
20 use crate::util::config::{
21     ClientConfig, ConnectorConfig, HttpConfig, HttpVersion, Proxy, Redirect, Timeout,
22 };
23 use crate::util::normalizer::RequestFormatter;
24 use crate::util::proxy::Proxies;
25 use crate::util::redirect::{RedirectInfo, Trigger};
26 
27 /// HTTP synchronous client implementation. Users can use `Client` to
28 /// send `Request` synchronously. `Client` depends on a `Connector` that
29 /// can be customized by the user.
30 ///
31 /// # Examples
32 ///
33 /// ```no_run
34 /// use ylong_http_client::sync_impl::{Client, EmptyBody, Request};
35 ///
36 /// // Creates a new `Client`.
37 /// let client = Client::new();
38 ///
39 /// // Creates a new `Request`.
40 /// let request = Request::new(EmptyBody);
41 ///
42 /// // Sends `Request` and block waiting for `Response` to return.
43 /// let response = client.request(request).unwrap();
44 ///
45 /// // Gets the content of `Response`.
46 /// let status = response.status();
47 /// ```
48 pub struct Client<C: Connector> {
49     inner: ConnPool<C, C::Stream>,
50     config: ClientConfig,
51 }
52 
53 impl Client<HttpConnector> {
54     /// Creates a new, default `Client`, which uses
55     /// [`sync_impl::HttpConnector`].
56     ///
57     /// [`sync_impl::HttpConnector`]: HttpConnector
58     ///
59     /// # Examples
60     ///
61     /// ```
62     /// use ylong_http_client::sync_impl::Client;
63     ///
64     /// let client = Client::new();
65     /// ```
new() -> Self66     pub fn new() -> Self {
67         Self::with_connector(HttpConnector::default())
68     }
69 
70     /// Creates a new, default [`sync_impl::ClientBuilder`].
71     ///
72     /// [`sync_impl::ClientBuilder`]: ClientBuilder
73     ///
74     /// # Examples
75     ///
76     /// ```
77     /// use ylong_http_client::sync_impl::Client;
78     ///
79     /// let builder = Client::builder();
80     /// ```
builder() -> ClientBuilder81     pub fn builder() -> ClientBuilder {
82         ClientBuilder::new()
83     }
84 }
85 
86 impl<C: Connector> Client<C> {
87     /// Creates a new, default `Client` with a given connector.
with_connector(connector: C) -> Self88     pub fn with_connector(connector: C) -> Self {
89         Self {
90             inner: ConnPool::new(connector),
91             config: ClientConfig::new(),
92         }
93     }
94 
95     /// Sends HTTP Request synchronously. This method will block the current
96     /// thread until a `Response` is obtained or an error occurs.
97     ///
98     /// # Examples
99     ///
100     /// ```no_run
101     /// use ylong_http_client::sync_impl::{Client, EmptyBody, Request};
102     ///
103     /// let client = Client::new();
104     /// let response = client.request(Request::new(EmptyBody));
105     /// ```
request<T: Body>( &self, mut request: Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>106     pub fn request<T: Body>(
107         &self,
108         mut request: Request<T>,
109     ) -> Result<Response<HttpBody>, HttpClientError> {
110         RequestFormatter::new(&mut request).format()?;
111         self.retry_send_request(request)
112     }
113 
retry_send_request<T: Body>( &self, mut request: Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>114     fn retry_send_request<T: Body>(
115         &self,
116         mut request: Request<T>,
117     ) -> Result<Response<HttpBody>, HttpClientError> {
118         let mut retries = self.config.retry.times().unwrap_or(0);
119         loop {
120             let response = self.send_request_retryable(&mut request);
121             if response.is_ok() || retries == 0 {
122                 return response;
123             }
124             retries -= 1;
125         }
126     }
127 
send_request_retryable<T: Body>( &self, request: &mut Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>128     fn send_request_retryable<T: Body>(
129         &self,
130         request: &mut Request<T>,
131     ) -> Result<Response<HttpBody>, HttpClientError> {
132         let response = self.send_request_with_uri(request.uri().clone(), request)?;
133         self.redirect_request(response, request)
134     }
135 
redirect_request<T: Body>( &self, response: Response<HttpBody>, request: &mut Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>136     fn redirect_request<T: Body>(
137         &self,
138         response: Response<HttpBody>,
139         request: &mut Request<T>,
140     ) -> Result<Response<HttpBody>, HttpClientError> {
141         let mut response = response;
142         let mut info = RedirectInfo::new();
143         loop {
144             match self
145                 .config
146                 .redirect
147                 .inner()
148                 .redirect(request, &mut response, &mut info)?
149             {
150                 Trigger::NextLink => {
151                     RequestFormatter::new(request).format()?;
152                     response = self.send_request_with_uri(request.uri().clone(), request)?;
153                 }
154                 Trigger::Stop => return Ok(response),
155             }
156         }
157     }
158 
send_request_with_uri<T: Body>( &self, uri: Uri, request: &mut Request<T>, ) -> Result<Response<HttpBody>, HttpClientError>159     fn send_request_with_uri<T: Body>(
160         &self,
161         uri: Uri,
162         request: &mut Request<T>,
163     ) -> Result<Response<HttpBody>, HttpClientError> {
164         conn::request(self.inner.connect_to(uri)?, request)
165     }
166 }
167 
168 impl Default for Client<HttpConnector> {
default() -> Self169     fn default() -> Self {
170         Self::new()
171     }
172 }
173 
174 /// A builder which is used to construct `sync_impl::Client`.
175 ///
176 /// # Examples
177 ///
178 /// ```
179 /// use ylong_http_client::sync_impl::ClientBuilder;
180 ///
181 /// let client = ClientBuilder::new().build();
182 /// ```
183 pub struct ClientBuilder {
184     /// Options and flags that is related to `HTTP`.
185     http: HttpConfig,
186 
187     /// Options and flags that is related to `Client`.
188     client: ClientConfig,
189 
190     /// Options and flags that is related to `Proxy`.
191     proxies: Proxies,
192 
193     /// Options and flags that is related to `TLS`.
194     #[cfg(feature = "__tls")]
195     tls: crate::util::TlsConfigBuilder,
196 }
197 
198 impl ClientBuilder {
199     /// Creates a new, default `ClientBuilder`.
200     ///
201     /// # Examples
202     ///
203     /// ```
204     /// use ylong_http_client::sync_impl::ClientBuilder;
205     ///
206     /// let builder = ClientBuilder::new();
207     /// ```
new() -> Self208     pub fn new() -> Self {
209         Self {
210             http: HttpConfig::default(),
211             client: ClientConfig::default(),
212             proxies: Proxies::default(),
213 
214             #[cfg(feature = "__tls")]
215             tls: crate::util::TlsConfig::builder(),
216         }
217     }
218 
219     /// Only use HTTP/1.
220     ///
221     /// # Examples
222     ///
223     /// ```
224     /// use ylong_http_client::sync_impl::ClientBuilder;
225     ///
226     /// let builder = ClientBuilder::new().http1_only();
227     /// ```
http1_only(mut self) -> Self228     pub fn http1_only(mut self) -> Self {
229         self.http.version = HttpVersion::Http1;
230         self
231     }
232 
233     /// Enables a request timeout.
234     ///
235     /// The timeout is applied from when the request starts connection util the
236     /// response body has finished.
237     ///
238     /// # Examples
239     ///
240     /// ```
241     /// use ylong_http_client::sync_impl::ClientBuilder;
242     /// use ylong_http_client::Timeout;
243     ///
244     /// let builder = ClientBuilder::new().request_timeout(Timeout::none());
245     /// ```
request_timeout(mut self, timeout: Timeout) -> Self246     pub fn request_timeout(mut self, timeout: Timeout) -> Self {
247         self.client.request_timeout = timeout;
248         self
249     }
250 
251     /// Sets a timeout for only the connect phase of `Client`.
252     ///
253     /// Default is `Timeout::none()`.
254     ///
255     /// # Examples
256     ///
257     /// ```
258     /// use ylong_http_client::sync_impl::ClientBuilder;
259     /// use ylong_http_client::Timeout;
260     ///
261     /// let builder = ClientBuilder::new().connect_timeout(Timeout::none());
262     /// ```
connect_timeout(mut self, timeout: Timeout) -> Self263     pub fn connect_timeout(mut self, timeout: Timeout) -> Self {
264         self.client.connect_timeout = timeout;
265         self
266     }
267 
268     /// Sets a `Redirect` for this client.
269     ///
270     /// Default will follow redirects up to a maximum of 10.
271     ///
272     /// # Examples
273     ///
274     /// ```
275     /// use ylong_http_client::sync_impl::ClientBuilder;
276     /// use ylong_http_client::Redirect;
277     ///
278     /// let builder = ClientBuilder::new().redirect(Redirect::none());
279     /// ```
redirect(mut self, redirect: Redirect) -> Self280     pub fn redirect(mut self, redirect: Redirect) -> Self {
281         self.client.redirect = redirect;
282         self
283     }
284 
285     /// Adds a `Proxy` to the list of proxies the `Client` will use.
286     ///
287     /// # Examples
288     ///
289     /// ```
290     /// # use ylong_http_client::sync_impl::ClientBuilder;
291     /// # use ylong_http_client::{HttpClientError, Proxy};
292     ///
293     /// # fn add_proxy() -> Result<(), HttpClientError> {
294     /// let builder = ClientBuilder::new().proxy(Proxy::http("http://www.example.com").build()?);
295     /// # Ok(())
296     /// # }
proxy(mut self, proxy: Proxy) -> Self297     pub fn proxy(mut self, proxy: Proxy) -> Self {
298         self.proxies.add_proxy(proxy.inner());
299         self
300     }
301 
302     /// Constructs a `Client` based on the given settings.
303     ///
304     /// # Examples
305     ///
306     /// ```
307     /// use ylong_http_client::sync_impl::ClientBuilder;
308     ///
309     /// let client = ClientBuilder::new().build();
310     /// ```
build(self) -> Result<Client<HttpConnector>, HttpClientError>311     pub fn build(self) -> Result<Client<HttpConnector>, HttpClientError> {
312         let config = ConnectorConfig {
313             proxies: self.proxies,
314             #[cfg(feature = "__tls")]
315             tls: self.tls.build()?,
316         };
317 
318         let connector = HttpConnector::new(config);
319 
320         Ok(Client {
321             inner: ConnPool::new(connector),
322             config: self.client,
323         })
324     }
325 }
326 
327 #[cfg(feature = "__tls")]
328 impl ClientBuilder {
329     /// Sets the maximum allowed TLS version for connections.
330     ///
331     /// By default there's no maximum.
332     ///
333     /// # Examples
334     ///
335     /// ```
336     /// use ylong_http_client::sync_impl::ClientBuilder;
337     /// use ylong_http_client::TlsVersion;
338     ///
339     /// let builder = ClientBuilder::new().max_tls_version(TlsVersion::TLS_1_2);
340     /// ```
max_tls_version(mut self, version: crate::util::TlsVersion) -> Self341     pub fn max_tls_version(mut self, version: crate::util::TlsVersion) -> Self {
342         self.tls = self.tls.max_proto_version(version);
343         self
344     }
345 
346     /// Sets the minimum required TLS version for connections.
347     ///
348     /// By default the TLS backend's own default is used.
349     ///
350     /// # Examples
351     ///
352     /// ```
353     /// use ylong_http_client::sync_impl::ClientBuilder;
354     /// use ylong_http_client::TlsVersion;
355     ///
356     /// let builder = ClientBuilder::new().min_tls_version(TlsVersion::TLS_1_2);
357     /// ```
min_tls_version(mut self, version: crate::util::TlsVersion) -> Self358     pub fn min_tls_version(mut self, version: crate::util::TlsVersion) -> Self {
359         self.tls = self.tls.min_proto_version(version);
360         self
361     }
362 
363     /// Adds a custom root certificate.
364     ///
365     /// This can be used to connect to a server that has a self-signed.
366     /// certificate for example.
367     ///
368     /// # Examples
369     ///
370     /// ```
371     /// use ylong_http_client::sync_impl::ClientBuilder;
372     /// use ylong_http_client::Certificate;
373     ///
374     /// # fn set_cert(cert: Certificate) {
375     /// let builder = ClientBuilder::new().add_root_certificate(cert);
376     /// # }
377     /// ```
add_root_certificate(mut self, certs: crate::util::Certificate) -> Self378     pub fn add_root_certificate(mut self, certs: crate::util::Certificate) -> Self {
379         use crate::c_openssl::adapter::CertificateList;
380 
381         match certs.into_inner() {
382             CertificateList::CertList(c) => {
383                 self.tls = self.tls.add_root_certificates(c);
384             }
385             #[cfg(feature = "c_openssl_3_0")]
386             CertificateList::PathList(p) => {
387                 self.tls = self.tls.add_path_certificates(p);
388             }
389         }
390         self
391     }
392 
393     /// Loads trusted root certificates from a file. The file should contain a
394     /// sequence of PEM-formatted CA certificates.
395     ///
396     /// # Examples
397     ///
398     /// ```
399     /// use ylong_http_client::sync_impl::ClientBuilder;
400     ///
401     /// let builder = ClientBuilder::new().tls_ca_file("ca.crt");
402     /// ```
tls_ca_file(mut self, path: &str) -> Self403     pub fn tls_ca_file(mut self, path: &str) -> Self {
404         self.tls = self.tls.ca_file(path);
405         self
406     }
407 
408     /// Sets the list of supported ciphers for protocols before `TLSv1.3`.
409     ///
410     /// See [`ciphers`] for details on the format.
411     ///
412     /// [`ciphers`]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html
413     ///
414     /// # Examples
415     ///
416     /// ```
417     /// use ylong_http_client::sync_impl::ClientBuilder;
418     ///
419     /// let builder = ClientBuilder::new()
420     ///     .tls_cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK");
421     /// ```
tls_cipher_list(mut self, list: &str) -> Self422     pub fn tls_cipher_list(mut self, list: &str) -> Self {
423         self.tls = self.tls.cipher_list(list);
424         self
425     }
426 
427     /// Sets the list of supported ciphers for the `TLSv1.3` protocol.
428     ///
429     /// The format consists of TLSv1.3 cipher suite names separated by `:`
430     /// characters in order of preference.
431     ///
432     /// Requires `OpenSSL 1.1.1` or `LibreSSL 3.4.0` or newer.
433     ///
434     /// # Examples
435     ///
436     /// ```
437     /// use ylong_http_client::sync_impl::ClientBuilder;
438     ///
439     /// let builder = ClientBuilder::new().tls_cipher_suites(
440     ///     "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK",
441     /// );
442     /// ```
tls_cipher_suites(mut self, list: &str) -> Self443     pub fn tls_cipher_suites(mut self, list: &str) -> Self {
444         self.tls = self.tls.cipher_suites(list);
445         self
446     }
447 
448     /// Controls the use of built-in system certificates during certificate
449     /// validation. Default to `true` -- uses built-in system certs.
450     ///
451     /// # Examples
452     ///
453     /// ```
454     /// use ylong_http_client::sync_impl::ClientBuilder;
455     ///
456     /// let builder = ClientBuilder::new().tls_built_in_root_certs(false);
457     /// ```
tls_built_in_root_certs(mut self, is_use: bool) -> Self458     pub fn tls_built_in_root_certs(mut self, is_use: bool) -> Self {
459         self.tls = self.tls.build_in_root_certs(is_use);
460         self
461     }
462 
463     /// Controls the use of certificates verification.
464     ///
465     /// Defaults to `false` -- verify certificates.
466     ///
467     /// # Warning
468     ///
469     /// When sets `true`, any certificate for any site will be trusted for use.
470     ///
471     /// # Examples
472     ///
473     /// ```
474     /// use ylong_http_client::sync_impl::ClientBuilder;
475     ///
476     /// let builder = ClientBuilder::new().danger_accept_invalid_certs(true);
477     /// ```
danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self478     pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self {
479         self.tls = self.tls.danger_accept_invalid_certs(is_invalid);
480         self
481     }
482 
483     /// Controls the use of hostname verification.
484     ///
485     /// Defaults to `false` -- verify hostname.
486     ///
487     /// # Warning
488     ///
489     /// When sets `true`, any valid certificate for any site will be trusted for
490     /// use from any other.
491     ///
492     /// # Examples
493     ///
494     /// ```
495     /// use ylong_http_client::sync_impl::ClientBuilder;
496     ///
497     /// let builder = ClientBuilder::new().danger_accept_invalid_hostnames(true);
498     /// ```
danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self499     pub fn danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self {
500         self.tls = self.tls.danger_accept_invalid_hostnames(is_invalid);
501         self
502     }
503 
504     /// Controls the use of TLS server name indication.
505     ///
506     /// Defaults to `true` -- sets sni.
507     ///
508     /// # Examples
509     ///
510     /// ```
511     /// use ylong_http_client::sync_impl::ClientBuilder;
512     ///
513     /// let builder = ClientBuilder::new().tls_sni(true);
514     /// ```
tls_sni(mut self, is_set_sni: bool) -> Self515     pub fn tls_sni(mut self, is_set_sni: bool) -> Self {
516         self.tls = self.tls.sni(is_set_sni);
517         self
518     }
519 }
520 
521 impl Default for ClientBuilder {
default() -> Self522     fn default() -> Self {
523         Self::new()
524     }
525 }
526 
527 #[cfg(test)]
528 mod ut_syn_client {
529     use ylong_http::body::TextBody;
530     use ylong_http::request::uri::Uri;
531     use ylong_http::request::Request;
532 
533     use crate::sync_impl::Client;
534 
535     /// UT test cases for `Client::request`.
536     ///
537     /// # Brief
538     /// 1. Creates a `Client` by calling `Client::new`.
539     /// 2. Calls `request`.
540     /// 3. Checks if the result is error.
541     #[test]
ut_request_client_err()542     fn ut_request_client_err() {
543         let client = Client::new();
544         let reader = "Hello World";
545         let body = TextBody::from_bytes(reader.as_bytes());
546         let mut req = Request::new(body);
547         let request_uri = req.uri_mut();
548         *request_uri = Uri::from_bytes(b"http://_:80").unwrap();
549         let response = client.request(req);
550         assert!(response.is_err())
551     }
552 
553     /// UT test cases for `Client::new`.
554     ///
555     /// # Brief
556     /// 1. Creates a `Client` by calling `Client::new`.
557     /// 2. Calls `request`.
558     /// 3. Checks if the result is correct.
559     #[test]
ut_client_new()560     fn ut_client_new() {
561         let _ = Client::default();
562         let _ = Client::new();
563     }
564 
565     /// UT test cases for `Client::builder`.
566     ///
567     /// # Brief
568     /// 1. Creates a `Client` by calling `Client::builder`.
569     /// 2. Calls `http_config`, `client_config`, `tls_config` and `build`
570     ///    respectively.
571     /// 3. Checks if the result is correct.
572     #[cfg(feature = "__tls")]
573     #[test]
ut_client_builder()574     fn ut_client_builder() {
575         let builder = Client::builder().build();
576         assert!(builder.is_ok());
577     }
578 }
579