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