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