From 5e7494a857bbfe07087b60345b6160f639e95a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BB=BA=E8=BE=9B?= Date: Tue, 11 Jul 2023 20:25:24 +0800 Subject: [PATCH] =?UTF-8?q?ylong=5Fhttp=20danger=20=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E8=A1=A5=E5=85=85(=E5=8F=AF=E8=AE=BE=E7=BD=AE=E4=B8=8D?= =?UTF-8?q?=E6=A3=80=E9=AA=8C=E8=AF=81=E4=B9=A6=EF=BC=8C=20=E4=B8=8D?= =?UTF-8?q?=E6=A3=80=E9=AA=8C=20hostname)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 张建辛 --- ylong_http_client/Cargo.toml | 7 +- ylong_http_client/examples/async_http.rs | 11 +- ylong_http_client/examples/async_http2.rs | 18 +- .../examples/async_http2_multi.rs | 15 +- .../examples/async_https_outside.rs | 30 +- .../examples/async_proxy_http.rs | 14 +- .../examples/async_redirect_http.rs | 16 +- ylong_http_client/examples/sync_http.rs | 11 +- .../examples/sync_https_outside.rs | 55 ++++ ylong_http_client/examples/sync_proxy_http.rs | 14 +- .../examples/sync_redirect_http.rs | 16 +- ylong_http_client/src/async_impl/client.rs | 122 +++++--- ylong_http_client/src/async_impl/connector.rs | 3 +- ylong_http_client/src/sync_impl/client.rs | 135 +++++++-- ylong_http_client/src/sync_impl/connector.rs | 15 +- .../src/util/c_openssl/adapter.rs | 275 ++++++++++++------ ylong_http_client/src/util/c_openssl/mod.rs | 13 - .../src/util/c_openssl/ssl/ctx.rs | 197 +++++-------- .../src/util/c_openssl/ssl/mod.rs | 2 +- .../src/util/c_openssl/ssl/ssl_base.rs | 10 +- ylong_http_client/tests/common/async_utils.rs | 3 +- ylong_http_client/tests/common/sync_utils.rs | 3 +- 22 files changed, 598 insertions(+), 387 deletions(-) create mode 100644 ylong_http_client/examples/sync_https_outside.rs diff --git a/ylong_http_client/Cargo.toml b/ylong_http_client/Cargo.toml index 1efc648..e964149 100644 --- a/ylong_http_client/Cargo.toml +++ b/ylong_http_client/Cargo.toml @@ -63,7 +63,12 @@ required-features = ["async", "http2", "tokio_base"] [[example]] name = "async_https_outside" path = "./examples/async_https_outside.rs" -required-features = ["async", "http1_1", "tls_default", "tokio_base"] +required-features = ["async", "http1_1", "__tls", "tokio_base"] + +[[example]] +name = "sync_https_outside" +path = "./examples/sync_https_outside.rs" +required-features = ["sync", "http1_1", "__tls"] [[example]] name = "async_proxy_http" diff --git a/ylong_http_client/examples/async_http.rs b/ylong_http_client/examples/async_http.rs index 401b4ea..b69d544 100644 --- a/ylong_http_client/examples/async_http.rs +++ b/ylong_http_client/examples/async_http.rs @@ -15,19 +15,22 @@ //! It demonstrates creating a client, making a request, and reading the response asynchronously. use ylong_http_client::async_impl::{Client, Downloader}; -use ylong_http_client::Request; +use ylong_http_client::{HttpClientError, Request}; #[tokio::main] -async fn main() { +async fn main() -> Result<(), HttpClientError> { // Creates a `async_impl::Client` let client = Client::new(); // Creates a `Request`. - let request = Request::get("127.0.0.1:3000").body("".as_bytes()).unwrap(); + let request = Request::get("127.0.0.1:3000") + .body("".as_bytes()) + .map_err(|e| HttpClientError::other(Some(e)))?; // Sends request and receives a `Response`. - let response = client.request(request).await.unwrap(); + let response = client.request(request).await?; // Reads the body of `Response` by using `BodyReader`. let _ = Downloader::console(response).download().await; + Ok(()) } diff --git a/ylong_http_client/examples/async_http2.rs b/ylong_http_client/examples/async_http2.rs index ad548cf..cb61855 100644 --- a/ylong_http_client/examples/async_http2.rs +++ b/ylong_http_client/examples/async_http2.rs @@ -15,30 +15,25 @@ //! It demonstrates creating a client, making a request, and reading the response asynchronously. use ylong_http_client::async_impl::{Body, ClientBuilder}; -use ylong_http_client::{RequestBuilder, StatusCode, TextBody, Version}; +use ylong_http_client::{HttpClientError, RequestBuilder, StatusCode, TextBody, Version}; -fn main() { +fn main() -> Result<(), HttpClientError> { let rt = tokio::runtime::Builder::new_multi_thread() .worker_threads(1) .enable_all() - .build() - .expect("Build runtime failed."); + .build()?; - let client = ClientBuilder::new() - .http2_prior_knowledge() - .build() - .unwrap(); + let client = ClientBuilder::new().http2_prior_knowledge().build()?; let request = RequestBuilder::new() .version("HTTP/2.0") .url("127.0.0.1:5678") .method("GET") .header("host", "127.0.0.1") - .body(TextBody::from_bytes("Hi".as_bytes())) - .unwrap(); + .body(TextBody::from_bytes("Hi".as_bytes()))?; rt.block_on(async move { - let mut response = client.request(request).await.unwrap(); + let mut response = client.request(request).await?; assert_eq!(response.version(), &Version::HTTP2); assert_eq!(response.status(), &StatusCode::OK); @@ -62,4 +57,5 @@ fn main() { "Assert response body failed" ); }); + Ok(()) } diff --git a/ylong_http_client/examples/async_http2_multi.rs b/ylong_http_client/examples/async_http2_multi.rs index d096206..daa4a02 100644 --- a/ylong_http_client/examples/async_http2_multi.rs +++ b/ylong_http_client/examples/async_http2_multi.rs @@ -16,19 +16,16 @@ use std::sync::Arc; use ylong_http_client::async_impl::{Body, ClientBuilder}; -use ylong_http_client::{RequestBuilder, StatusCode, TextBody, Version}; +use ylong_http_client::{HttpClientError, RequestBuilder, StatusCode, TextBody, Version}; -fn main() { +fn main() -> Result<(), HttpClientError> { let rt = tokio::runtime::Builder::new_multi_thread() .worker_threads(1) .enable_all() .build() .expect("Build runtime failed."); - let client = ClientBuilder::new() - .http2_prior_knowledge() - .build() - .unwrap(); + let client = ClientBuilder::new().http2_prior_knowledge().build()?; let client_interface = Arc::new(client); let mut shut_downs = vec![]; @@ -42,10 +39,9 @@ fn main() { .url("127.0.0.1:5678") .method("GET") .header("host", "127.0.0.1") - .body(TextBody::from_bytes(body_text.as_bytes())) - .unwrap(); + .body(TextBody::from_bytes(body_text.as_bytes()))?; - let mut response = client.request(request).await.unwrap(); + let mut response = client.request(request).await?; assert_eq!(response.version(), &Version::HTTP2); assert_eq!(response.status(), &StatusCode::OK); @@ -77,4 +73,5 @@ fn main() { rt.block_on(shut_down) .expect("Runtime wait for server shutdown failed"); } + Ok(()) } diff --git a/ylong_http_client/examples/async_https_outside.rs b/ylong_http_client/examples/async_https_outside.rs index 40e10a8..c382823 100644 --- a/ylong_http_client/examples/async_https_outside.rs +++ b/ylong_http_client/examples/async_https_outside.rs @@ -13,7 +13,7 @@ use ylong_http_client::async_impl::{Client, Downloader}; use ylong_http_client::util::Redirect; -use ylong_http_client::{Certificate, Request, TlsVersion}; +use ylong_http_client::{Certificate, HttpClientError, Request, TlsVersion}; fn main() { let rt = tokio::runtime::Builder::new_multi_thread() @@ -21,7 +21,7 @@ fn main() { .build() .expect("Tokio runtime build err."); let mut v = vec![]; - for _i in 0..1 { + for _i in 0..3 { let handle = rt.spawn(req()); v.push(handle); } @@ -33,34 +33,32 @@ fn main() { }); } -async fn req() { +async fn req() -> Result<(), HttpClientError> { let v = "some certs".as_bytes(); - let cert = Certificate::from_pem(v); + let cert = Certificate::from_pem(v)?; + // Creates a `async_impl::Client` let client = Client::builder() .redirect(Redirect::default()) - .tls_built_in_root_certs(true) // default true + .tls_built_in_root_certs(false) // not use root certs + .danger_accept_invalid_certs(true) // not verify certs .max_tls_version(TlsVersion::TLS_1_2) .min_tls_version(TlsVersion::TLS_1_2) - .add_root_certificate(cert.unwrap()) - .build() - .unwrap(); + .add_root_certificate(cert) + .build()?; // Creates a `Request`. - let request = Request::get( - "https://www.baidu.com", - // "https://app.prntscr.com/build/setup-lightshot.exe", - ) - .body("".as_bytes()) - .unwrap(); + let request = Request::get("https://www.baidu.com") + .body("".as_bytes()) + .map_err(|e| HttpClientError::other(Some(e)))?; // Sends request and receives a `Response`. - let response = client.request(request).await.unwrap(); - // assert!(response.is_ok()); + let response = client.request(request).await?; println!("{}", response.status().as_u16()); println!("{}", response.headers()); // Reads the body of `Response` by using `BodyReader`. let _ = Downloader::console(response).download().await; + Ok(()) } diff --git a/ylong_http_client/examples/async_proxy_http.rs b/ylong_http_client/examples/async_proxy_http.rs index 91e6524..ca71a6b 100644 --- a/ylong_http_client/examples/async_proxy_http.rs +++ b/ylong_http_client/examples/async_proxy_http.rs @@ -14,21 +14,21 @@ //! This is a simple asynchronous HTTP client example using the ylong_http_client crate. //! It demonstrates creating a client, making a request, and reading the response asynchronously. use ylong_http_client::async_impl::{ClientBuilder, Downloader}; -use ylong_http_client::{EmptyBody, Proxy, Request}; +use ylong_http_client::{EmptyBody, HttpClientError, Proxy, Request}; #[tokio::main] -async fn main() { +async fn main() -> Result<(), HttpClientError> { // Creates a `async_impl::Client` let client = ClientBuilder::new() - .proxy(Proxy::all("http://proxy.example.com").build().unwrap()) - .build() - .unwrap(); + .proxy(Proxy::all("http://proxy.example.com").build()?) + .build()?; // Creates a `Request`. let request = Request::get("http://127.0.0.1:3000") .body(EmptyBody) - .unwrap(); + .map_err(|e| HttpClientError::other(Some(e)))?; // Sends request and receives a `Response`. - let response = client.request(request).await.unwrap(); + let response = client.request(request).await?; // Reads the body of `Response` by using `BodyReader`. let _ = Downloader::console(response).download().await; + Ok(()) } diff --git a/ylong_http_client/examples/async_redirect_http.rs b/ylong_http_client/examples/async_redirect_http.rs index 0feffaf..500b984 100644 --- a/ylong_http_client/examples/async_redirect_http.rs +++ b/ylong_http_client/examples/async_redirect_http.rs @@ -15,20 +15,20 @@ //! It demonstrates creating a client, making a request, and reading the response asynchronously. use ylong_http_client::async_impl::{ClientBuilder, Downloader}; -use ylong_http_client::{Redirect, Request}; +use ylong_http_client::{HttpClientError, Redirect, Request}; #[tokio::main] -async fn main() { +async fn main() -> Result<(), HttpClientError> { // Creates a `async_impl::Client` - let client = ClientBuilder::new() - .redirect(Redirect::default()) - .build() - .unwrap(); + let client = ClientBuilder::new().redirect(Redirect::default()).build()?; // Creates a `Request`. - let request = Request::get("127.0.0.1:3000").body("".as_bytes()).unwrap(); + let request = Request::get("127.0.0.1:3000") + .body("".as_bytes()) + .map_err(|e| HttpClientError::other(Some(e)))?; // Sends request and receives a `Response`. - let response = client.request(request).await.unwrap(); + let response = client.request(request).await?; // Reads the body of `Response` by using `BodyReader`. let _ = Downloader::console(response).download().await; + Ok(()) } diff --git a/ylong_http_client/examples/sync_http.rs b/ylong_http_client/examples/sync_http.rs index 76a2b3c..7b89bec 100644 --- a/ylong_http_client/examples/sync_http.rs +++ b/ylong_http_client/examples/sync_http.rs @@ -15,18 +15,21 @@ //! It demonstrates creating a client, making a request, and reading the response. use ylong_http_client::sync_impl::{BodyReader, Client}; -use ylong_http_client::Request; +use ylong_http_client::{HttpClientError, Request}; -fn main() { +fn main() -> Result<(), HttpClientError> { // Creates a `sync_impl::Client` let client = Client::new(); // Creates a `Request`. - let request = Request::get("127.0.0.1:3000").body("".as_bytes()).unwrap(); + let request = Request::get("127.0.0.1:3000") + .body("".as_bytes()) + .map_err(|e| HttpClientError::other(Some(e)))?; // Sends request and receives a `Response`. - let mut response = client.request(request).unwrap(); + let mut response = client.request(request)?; // Reads the body of `Response` by using `BodyReader`. let _ = BodyReader::default().read_all(response.body_mut()); + Ok(()) } diff --git a/ylong_http_client/examples/sync_https_outside.rs b/ylong_http_client/examples/sync_https_outside.rs new file mode 100644 index 0000000..cbba111 --- /dev/null +++ b/ylong_http_client/examples/sync_https_outside.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2023 Huawei Device Co., Ltd. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use ylong_http_client::sync_impl::Client; +use ylong_http_client::util::Redirect; +use ylong_http_client::{Certificate, HttpClientError, Request, TlsVersion}; + +fn main() { + let mut v = vec![]; + for _i in 0..3 { + let handle = std::thread::spawn(|| req); + v.push(handle); + } + + for h in v { + let _ = h.join(); + } +} + +fn req() -> Result<(), HttpClientError> { + let v = "some certs".as_bytes(); + let cert = Certificate::from_pem(v)?; + + // Creates a `async_impl::Client` + let client = Client::builder() + .redirect(Redirect::default()) + .tls_built_in_root_certs(false) // not use root certs + .danger_accept_invalid_certs(true) // not verify certs + .max_tls_version(TlsVersion::TLS_1_2) + .min_tls_version(TlsVersion::TLS_1_2) + .add_root_certificate(cert) + .build()?; + + // Creates a `Request`. + let request = Request::get("https://www.baidu.com") + .body("".as_bytes()) + .map_err(|e| HttpClientError::other(Some(e)))?; + + // Sends request and receives a `Response`. + let response = client.request(request)?; + + println!("{}", response.status().as_u16()); + println!("{}", response.headers()); + Ok(()) +} diff --git a/ylong_http_client/examples/sync_proxy_http.rs b/ylong_http_client/examples/sync_proxy_http.rs index 4fe9496..216c537 100644 --- a/ylong_http_client/examples/sync_proxy_http.rs +++ b/ylong_http_client/examples/sync_proxy_http.rs @@ -15,22 +15,22 @@ //! It demonstrates creating a client, making a request, and reading the response. use ylong_http_client::sync_impl::{BodyReader, ClientBuilder}; use ylong_http_client::util::Proxy; -use ylong_http_client::{EmptyBody, Request}; +use ylong_http_client::{EmptyBody, HttpClientError, Request}; -fn main() { +fn main() -> Result<(), HttpClientError> { // Creates a `sync_impl::Client` let client = ClientBuilder::new() - .proxy(Proxy::http("https://proxy.example.com").build().unwrap()) - .build() - .unwrap(); + .proxy(Proxy::http("https://proxy.example.com").build()?) + .build()?; // Creates a `Request`. let request = Request::get("http://127.0.0.1:3000") .body(EmptyBody) - .unwrap(); + .map_err(|e| HttpClientError::other(Some(e)))?; // Sends request and receives a `Response`. - let mut response = client.request(request).unwrap(); + let mut response = client.request(request)?; // Reads the body of `Response` by using `BodyReader`. let _ = BodyReader::default().read_all(response.body_mut()); + Ok(()) } diff --git a/ylong_http_client/examples/sync_redirect_http.rs b/ylong_http_client/examples/sync_redirect_http.rs index 2061e6e..8dc72da 100644 --- a/ylong_http_client/examples/sync_redirect_http.rs +++ b/ylong_http_client/examples/sync_redirect_http.rs @@ -16,21 +16,21 @@ use ylong_http_client::sync_impl::{BodyReader, ClientBuilder}; use ylong_http_client::util::Redirect; -use ylong_http_client::Request; +use ylong_http_client::{HttpClientError, Request}; -fn main() { +fn main() -> Result<(), HttpClientError> { // Creates a `sync_impl::Client` - let client = ClientBuilder::new() - .redirect(Redirect::default()) - .build() - .unwrap(); + let client = ClientBuilder::new().redirect(Redirect::default()).build()?; // Creates a `Request`. - let request = Request::get("127.0.0.1:3000").body("".as_bytes()).unwrap(); + let request = Request::get("127.0.0.1:3000") + .body("".as_bytes()) + .map_err(|e| HttpClientError::other(Some(e)))?; // Sends request and receives a `Response`. - let mut response = client.request(request).unwrap(); + let mut response = client.request(request)?; // Reads the body of `Response` by using `BodyReader`. let _ = BodyReader::default().read_all(response.body_mut()); + Ok(()) } diff --git a/ylong_http_client/src/async_impl/client.rs b/ylong_http_client/src/async_impl/client.rs index c58f927..15970b5 100644 --- a/ylong_http_client/src/async_impl/client.rs +++ b/ylong_http_client/src/async_impl/client.rs @@ -459,6 +459,34 @@ impl ClientBuilder { self } + /// Constructs a `Client` based on the given settings. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::async_impl::ClientBuilder; + /// + /// let client = ClientBuilder::new().build(); + /// ``` + pub fn build(self) -> Result, HttpClientError> { + let config = ConnectorConfig { + proxies: self.proxies, + #[cfg(feature = "__tls")] + tls: self.tls.build()?, + }; + + let connector = HttpConnector::new(config); + + Ok(Client { + inner: ConnPool::new(self.http.clone(), connector), + client_config: self.client, + http_config: self.http, + }) + } +} + +#[cfg(feature = "__tls")] +impl ClientBuilder { /// Sets the maximum allowed TLS version for connections. /// /// By default there's no maximum. @@ -471,9 +499,8 @@ impl ClientBuilder { /// /// let builder = ClientBuilder::new().max_tls_version(TlsVersion::TLS_1_2); /// ``` - #[cfg(feature = "__tls")] pub fn max_tls_version(mut self, version: crate::util::TlsVersion) -> Self { - self.tls = self.tls.set_max_proto_version(version); + self.tls = self.tls.max_proto_version(version); self } @@ -489,9 +516,8 @@ impl ClientBuilder { /// /// let builder = ClientBuilder::new().min_tls_version(TlsVersion::TLS_1_2); /// ``` - #[cfg(feature = "__tls")] pub fn min_tls_version(mut self, version: crate::util::TlsVersion) -> Self { - self.tls = self.tls.set_min_proto_version(version); + self.tls = self.tls.min_proto_version(version); self } @@ -510,7 +536,6 @@ impl ClientBuilder { /// let builder = ClientBuilder::new().add_root_certificate(cert); /// # } /// ``` - #[cfg(feature = "__tls")] pub fn add_root_certificate(mut self, certs: crate::util::Certificate) -> Self { self.tls = self.tls.add_root_certificates(certs); self @@ -524,11 +549,10 @@ impl ClientBuilder { /// ``` /// use ylong_http_client::async_impl::ClientBuilder; /// - /// let builder = ClientBuilder::new().set_ca_file("ca.crt"); + /// let builder = ClientBuilder::new().tls_ca_file("ca.crt"); /// ``` - #[cfg(feature = "__tls")] - pub fn set_ca_file(mut self, path: &str) -> Self { - self.tls = self.tls.set_ca_file(path); + pub fn tls_ca_file(mut self, path: &str) -> Self { + self.tls = self.tls.ca_file(path); self } @@ -544,13 +568,12 @@ impl ClientBuilder { /// use ylong_http_client::async_impl::ClientBuilder; /// /// let builder = ClientBuilder::new() - /// .set_cipher_list( + /// .tls_cipher_list( /// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK" /// ); /// ``` - #[cfg(feature = "__tls")] - pub fn set_cipher_list(mut self, list: &str) -> Self { - self.tls = self.tls.set_cipher_list(list); + pub fn tls_cipher_list(mut self, list: &str) -> Self { + self.tls = self.tls.cipher_list(list); self } @@ -567,13 +590,12 @@ impl ClientBuilder { /// use ylong_http_client::async_impl::ClientBuilder; /// /// let builder = ClientBuilder::new() - /// .set_cipher_suites( + /// .tls_cipher_suites( /// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK" /// ); /// ``` - #[cfg(feature = "__tls")] - pub fn set_cipher_suites(mut self, list: &str) -> Self { - self.tls = self.tls.set_cipher_suites(list); + pub fn tls_cipher_suites(mut self, list: &str) -> Self { + self.tls = self.tls.cipher_suites(list); self } @@ -588,35 +610,69 @@ impl ClientBuilder { /// let builder = ClientBuilder::new() /// .tls_built_in_root_certs(false); /// ``` - #[cfg(feature = "__tls")] - pub fn tls_built_in_root_certs(mut self, is_use: bool) -> ClientBuilder { + pub fn tls_built_in_root_certs(mut self, is_use: bool) -> Self { self.tls = self.tls.build_in_root_certs(is_use); self } - /// Constructs a `Client` based on the given settings. + /// Controls the use of certificates verification. + /// + /// Defaults to `false` -- verify certificates. + /// + /// # Warning + /// + /// When sets `true`, any certificate for any site will be trusted for use. /// /// # Examples /// /// ``` /// use ylong_http_client::async_impl::ClientBuilder; /// - /// let client = ClientBuilder::new().build(); + /// let builder = ClientBuilder::new() + /// .danger_accept_invalid_certs(true); /// ``` - pub fn build(self) -> Result, HttpClientError> { - let config = ConnectorConfig { - proxies: self.proxies, - #[cfg(feature = "__tls")] - tls: self.tls.build()?, - }; + pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self { + self.tls = self.tls.danger_accept_invalid_certs(is_invalid); + self + } - let connector = HttpConnector::new(config); + /// Controls the use of hostname verification. + /// + /// Defaults to `false` -- verify hostname. + /// + /// # Warning + /// + /// When sets `true`, any valid certificate for any site will be trusted for + /// use from any other. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::async_impl::ClientBuilder; + /// + /// let builder = ClientBuilder::new() + /// .danger_accept_invalid_hostnames(true); + /// ``` + pub fn danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self { + self.tls = self.tls.danger_accept_invalid_hostnames(is_invalid); + self + } - Ok(Client { - inner: ConnPool::new(self.http.clone(), connector), - client_config: self.client, - http_config: self.http, - }) + /// Controls the use of TLS server name indication. + /// + /// Defaults to `true` -- sets sni. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::async_impl::ClientBuilder; + /// + /// let builder = ClientBuilder::new() + /// .tls_sni(true); + /// ``` + pub fn tls_sni(mut self, is_set_sni: bool) -> Self { + self.tls = self.tls.sni(is_set_sni); + self } } diff --git a/ylong_http_client/src/async_impl/connector.rs b/ylong_http_client/src/async_impl/connector.rs index 7f1e54a..8802fa7 100644 --- a/ylong_http_client/src/async_impl/connector.rs +++ b/ylong_http_client/src/async_impl/connector.rs @@ -150,8 +150,7 @@ mod tls { }; let mut stream = config - .ssl() - .and_then(|mut ssl| ssl.set_sni_verify(&host_name).map(|_| ssl)) + .ssl_new(&host_name) .and_then(|ssl| AsyncSslStream::new(ssl.into_inner(), tcp)) .map_err(|e| Error::new(ErrorKind::Other, e))?; diff --git a/ylong_http_client/src/sync_impl/client.rs b/ylong_http_client/src/sync_impl/client.rs index a811130..371b42c 100644 --- a/ylong_http_client/src/sync_impl/client.rs +++ b/ylong_http_client/src/sync_impl/client.rs @@ -34,7 +34,7 @@ use ylong_http::response::Response; /// /// ```no_run /// use ylong_http_client::sync_impl::Client; -/// use ylong_http_client::{Request, Response, EmptyBody}; +/// use ylong_http_client::{Request, EmptyBody}; /// /// // Creates a new `Client`. /// let client = Client::new(); @@ -312,6 +312,33 @@ impl ClientBuilder { self } + /// Constructs a `Client` based on the given settings. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::sync_impl::ClientBuilder; + /// + /// let client = ClientBuilder::new().build(); + /// ``` + pub fn build(self) -> Result, HttpClientError> { + let config = ConnectorConfig { + proxies: self.proxies, + #[cfg(feature = "__tls")] + tls: self.tls.build()?, + }; + + let connector = HttpConnector::new(config); + + Ok(Client { + inner: ConnPool::new(connector), + client_config: self.client, + }) + } +} + +#[cfg(feature = "__tls")] +impl ClientBuilder { /// Sets the maximum allowed TLS version for connections. /// /// By default there's no maximum. @@ -324,9 +351,8 @@ impl ClientBuilder { /// /// let builder = ClientBuilder::new().max_tls_version(TlsVersion::TLS_1_2); /// ``` - #[cfg(feature = "__tls")] pub fn max_tls_version(mut self, version: crate::util::TlsVersion) -> Self { - self.tls = self.tls.set_max_proto_version(version); + self.tls = self.tls.max_proto_version(version); self } @@ -342,9 +368,8 @@ impl ClientBuilder { /// /// let builder = ClientBuilder::new().min_tls_version(TlsVersion::TLS_1_2); /// ``` - #[cfg(feature = "__tls")] pub fn min_tls_version(mut self, version: crate::util::TlsVersion) -> Self { - self.tls = self.tls.set_min_proto_version(version); + self.tls = self.tls.min_proto_version(version); self } @@ -363,7 +388,6 @@ impl ClientBuilder { /// let builder = ClientBuilder::new().add_root_certificate(cert); /// # } /// ``` - #[cfg(feature = "__tls")] pub fn add_root_certificate(mut self, certs: crate::util::Certificate) -> Self { self.tls = self.tls.add_root_certificates(certs); self @@ -377,11 +401,10 @@ impl ClientBuilder { /// ``` /// use ylong_http_client::sync_impl::ClientBuilder; /// - /// let builder = ClientBuilder::new().set_ca_file("ca.crt"); + /// let builder = ClientBuilder::new().tls_ca_file("ca.crt"); /// ``` - #[cfg(feature = "__tls")] - pub fn set_ca_file(mut self, path: &str) -> Self { - self.tls = self.tls.set_ca_file(path); + pub fn tls_ca_file(mut self, path: &str) -> Self { + self.tls = self.tls.ca_file(path); self } @@ -397,13 +420,12 @@ impl ClientBuilder { /// use ylong_http_client::sync_impl::ClientBuilder; /// /// let builder = ClientBuilder::new() - /// .set_cipher_list( + /// .tls_cipher_list( /// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK" /// ); /// ``` - #[cfg(feature = "__tls")] - pub fn set_cipher_list(mut self, list: &str) -> Self { - self.tls = self.tls.set_cipher_list(list); + pub fn tls_cipher_list(mut self, list: &str) -> Self { + self.tls = self.tls.cipher_list(list); self } @@ -420,38 +442,89 @@ impl ClientBuilder { /// use ylong_http_client::sync_impl::ClientBuilder; /// /// let builder = ClientBuilder::new() - /// .set_cipher_suites( + /// .tls_cipher_suites( /// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK" /// ); /// ``` - #[cfg(feature = "__tls")] - pub fn set_cipher_suites(mut self, list: &str) -> Self { - self.tls = self.tls.set_cipher_suites(list); + pub fn tls_cipher_suites(mut self, list: &str) -> Self { + self.tls = self.tls.cipher_suites(list); self } - /// Constructs a `Client` based on the given settings. + /// Controls the use of built-in system certificates during certificate validation. + /// Default to `true` -- uses built-in system certs. /// /// # Examples /// /// ``` /// use ylong_http_client::sync_impl::ClientBuilder; /// - /// let client = ClientBuilder::new().build(); + /// let builder = ClientBuilder::new() + /// .tls_built_in_root_certs(false); /// ``` - pub fn build(self) -> Result, HttpClientError> { - let config = ConnectorConfig { - proxies: self.proxies, - #[cfg(feature = "__tls")] - tls: self.tls.build()?, - }; + pub fn tls_built_in_root_certs(mut self, is_use: bool) -> Self { + self.tls = self.tls.build_in_root_certs(is_use); + self + } - let connector = HttpConnector::new(config); + /// Controls the use of certificates verification. + /// + /// Defaults to `false` -- verify certificates. + /// + /// # Warning + /// + /// When sets `true`, any certificate for any site will be trusted for use. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::sync_impl::ClientBuilder; + /// + /// let builder = ClientBuilder::new() + /// .danger_accept_invalid_certs(true); + /// ``` + pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self { + self.tls = self.tls.danger_accept_invalid_certs(is_invalid); + self + } - Ok(Client { - inner: ConnPool::new(connector), - client_config: self.client, - }) + /// Controls the use of hostname verification. + /// + /// Defaults to `false` -- verify hostname. + /// + /// # Warning + /// + /// When sets `true`, any valid certificate for any site will be trusted for + /// use from any other. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::sync_impl::ClientBuilder; + /// + /// let builder = ClientBuilder::new() + /// .danger_accept_invalid_hostnames(true); + /// ``` + pub fn danger_accept_invalid_hostnames(mut self, is_invalid: bool) -> Self { + self.tls = self.tls.danger_accept_invalid_hostnames(is_invalid); + self + } + + /// Controls the use of TLS server name indication. + /// + /// Defaults to `true` -- sets sni. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::sync_impl::ClientBuilder; + /// + /// let builder = ClientBuilder::new() + /// .tls_sni(true); + /// ``` + pub fn tls_sni(mut self, is_set_sni: bool) -> Self { + self.tls = self.tls.sni(is_set_sni); + self } } diff --git a/ylong_http_client/src/sync_impl/connector.rs b/ylong_http_client/src/sync_impl/connector.rs index 5e582d1..b16d4f0 100644 --- a/ylong_http_client/src/sync_impl/connector.rs +++ b/ylong_http_client/src/sync_impl/connector.rs @@ -124,24 +124,13 @@ pub mod tls_conn { tcp_stream }; - let mut tls_ssl = self.config.tls.ssl().map_err(|e| { + let tls_ssl = self.config.tls.ssl_new(&host_name).map_err(|e| { HttpClientError::new_with_cause(ErrorKind::Connect, Some(e)) })?; - tls_ssl.set_sni_verify(&host_name).map_err(|e| { + let stream = tls_ssl.into_inner().connect(tcp_stream).map_err(|e| { HttpClientError::new_with_cause(ErrorKind::Connect, Some(e)) })?; - - let stream = self - .config - .tls - .ssl() - .map_err(|e| HttpClientError::new_with_cause(ErrorKind::Connect, Some(e)))? - .into_inner() - .connect(tcp_stream) - .map_err(|e| { - HttpClientError::new_with_cause(ErrorKind::Connect, Some(e)) - })?; Ok(MixStream::Https(stream)) } } diff --git a/ylong_http_client/src/util/c_openssl/adapter.rs b/ylong_http_client/src/util/c_openssl/adapter.rs index 5701610..00b50b4 100644 --- a/ylong_http_client/src/util/c_openssl/adapter.rs +++ b/ylong_http_client/src/util/c_openssl/adapter.rs @@ -30,14 +30,16 @@ use std::{net::IpAddr, path::Path}; /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion}; /// /// let context = TlsConfigBuilder::new() -/// .set_ca_file("ca.crt") -/// .set_max_proto_version(TlsVersion::TLS_1_2) -/// .set_min_proto_version(TlsVersion::TLS_1_2) -/// .set_cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") +/// .ca_file("ca.crt") +/// .max_proto_version(TlsVersion::TLS_1_2) +/// .min_proto_version(TlsVersion::TLS_1_2) +/// .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") /// .build(); /// ``` pub struct TlsConfigBuilder { - inner: SslContextBuilder, + inner: Result, + use_sni: bool, + verify_hostname: bool, } impl TlsConfigBuilder { @@ -53,6 +55,8 @@ impl TlsConfigBuilder { pub fn new() -> Self { Self { inner: SslContext::builder(SslMethod::tls_client()), + use_sni: true, + verify_hostname: true, } } @@ -65,10 +69,12 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::TlsConfigBuilder; /// /// let builder = TlsConfigBuilder::new() - /// .set_ca_file("ca.crt"); + /// .ca_file("ca.crt"); /// ``` - pub fn set_ca_file>(mut self, path: T) -> Self { - self.inner = self.inner.set_ca_file(path); + pub fn ca_file>(mut self, path: T) -> Self { + self.inner = self + .inner + .and_then(|mut builder| builder.set_ca_file(path).map(|_| builder)); self } @@ -83,10 +89,14 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion}; /// /// let builder = TlsConfigBuilder::new() - /// .set_max_proto_version(TlsVersion::TLS_1_2); + /// .max_proto_version(TlsVersion::TLS_1_2); /// ``` - pub fn set_max_proto_version(mut self, version: TlsVersion) -> Self { - self.inner = self.inner.set_max_proto_version(version.into_inner()); + pub fn max_proto_version(mut self, version: TlsVersion) -> Self { + self.inner = self.inner.and_then(|mut builder| { + builder + .set_max_proto_version(version.into_inner()) + .map(|_| builder) + }); self } @@ -101,10 +111,14 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion}; /// /// let builder = TlsConfigBuilder::new() - /// .set_min_proto_version(TlsVersion::TLS_1_2); + /// .min_proto_version(TlsVersion::TLS_1_2); /// ``` - pub fn set_min_proto_version(mut self, version: TlsVersion) -> Self { - self.inner = self.inner.set_min_proto_version(version.into_inner()); + pub fn min_proto_version(mut self, version: TlsVersion) -> Self { + self.inner = self.inner.and_then(|mut builder| { + builder + .set_min_proto_version(version.into_inner()) + .map(|_| builder) + }); self } @@ -122,12 +136,14 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::TlsConfigBuilder; /// /// let builder = TlsConfigBuilder::new() - /// .set_cipher_list( + /// .cipher_list( /// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK" /// ); /// ``` - pub fn set_cipher_list(mut self, list: &str) -> Self { - self.inner = self.inner.set_cipher_list(list); + pub fn cipher_list(mut self, list: &str) -> Self { + self.inner = self + .inner + .and_then(|mut builder| builder.set_cipher_list(list).map(|_| builder)); self } @@ -147,12 +163,14 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::TlsConfigBuilder; /// /// let builder = TlsConfigBuilder::new() - /// .set_cipher_suites( + /// .cipher_suites( /// "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK" /// ); /// ``` - pub fn set_cipher_suites(mut self, list: &str) -> Self { - self.inner = self.inner.set_cipher_suites(list); + pub fn cipher_suites(mut self, list: &str) -> Self { + self.inner = self + .inner + .and_then(|mut builder| builder.set_cipher_suites(list).map(|_| builder)); self } @@ -168,12 +186,14 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::{TlsConfigBuilder, TlsFileType}; /// /// let builder = TlsConfigBuilder::new() - /// .set_certificate_file("cert.pem", TlsFileType::PEM); + /// .certificate_file("cert.pem", TlsFileType::PEM); /// ``` - pub fn set_certificate_file>(mut self, path: T, file_type: TlsFileType) -> Self { - self.inner = self - .inner - .set_certificate_file(path, file_type.into_inner()); + pub fn certificate_file>(mut self, path: T, file_type: TlsFileType) -> Self { + self.inner = self.inner.and_then(|mut builder| { + builder + .set_certificate_file(path, file_type.into_inner()) + .map(|_| builder) + }); self } @@ -188,29 +208,23 @@ impl TlsConfigBuilder { /// ``` /// use ylong_http_client::util::TlsConfigBuilder; /// - /// let builder = TlsConfigBuilder::new().set_certificate_chain_file("cert.pem"); + /// let builder = TlsConfigBuilder::new().certificate_chain_file("cert.pem"); /// ``` - pub fn set_certificate_chain_file>(mut self, path: T) -> Self { - self.inner = self.inner.set_certificate_chain_file(path); + pub fn certificate_chain_file>(mut self, path: T) -> Self { + self.inner = self + .inner + .and_then(|mut builder| builder.set_certificate_chain_file(path).map(|_| builder)); self } /// Adds custom root certificate. pub fn add_root_certificates(mut self, certs: Certificate) -> Self { for cert in certs.inner { - let store = match self.inner.cert_store_mut() { - Ok(store) => store, - Err(e) => { - self.inner = SslContextBuilder::from_error(e); - return self; - } - }; - if let Err(e) = store.add_cert(cert.0) { - self.inner = SslContextBuilder::from_error(e); - return self; - } + self.inner = self.inner.and_then(|mut builder| { + { Ok(builder.cert_store_mut()).map(|store| store.add_cert(cert.0)) } + .map(|_| builder) + }); } - self } @@ -223,10 +237,12 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::{TlsConfigBuilder}; /// /// let protocols = b"\x06spdy/1\x08http/1.1"; - /// let builder = TlsConfigBuilder::new().set_alpn_protos(protocols); + /// let builder = TlsConfigBuilder::new().alpn_protos(protocols); /// ``` - pub fn set_alpn_protos(mut self, protocols: &[u8]) -> Self { - self.inner = self.inner.set_alpn_protos(protocols); + pub fn alpn_protos(mut self, protocols: &[u8]) -> Self { + self.inner = self + .inner + .and_then(|mut builder| builder.set_alpn_protos(protocols).map(|_| builder)); self } @@ -243,10 +259,12 @@ impl TlsConfigBuilder { /// let protocols = AlpnProtocolList::new() /// .extend(AlpnProtocol::SPDY1) /// .extend(AlpnProtocol::HTTP11); - /// let builder = TlsConfigBuilder::new().set_alpn_protos(protocols.as_slice()); + /// let builder = TlsConfigBuilder::new().alpn_protos(protocols.as_slice()); /// ``` - pub fn set_alpn_proto_list(mut self, list: AlpnProtocolList) -> Self { - self.inner = self.inner.set_alpn_protos(list.as_slice()); + pub fn alpn_proto_list(mut self, list: AlpnProtocolList) -> Self { + self.inner = self + .inner + .and_then(|mut builder| builder.set_alpn_protos(list.as_slice()).map(|_| builder)); self } @@ -254,15 +272,84 @@ impl TlsConfigBuilder { /// Default to `true` -- uses built-in system certs. pub fn build_in_root_certs(mut self, is_use: bool) -> Self { if !is_use { - let cert_store = X509Store::new(); - match cert_store { - Ok(store) => self.inner = self.inner.set_cert_store(store), - Err(e) => self.inner = SslContextBuilder::from_error(e), - } + self.inner = X509Store::new().and_then(|store| { + self.inner.and_then(|mut builder| { + { + builder.set_cert_store(store); + Ok(()) + } + .map(|_| builder) + }) + }); } self } + /// Controls the use of certificates verification. + /// + /// Defaults to `false` -- verify certificates. + /// + /// # Warning + /// + /// When sets `true`, any certificate for any site will be trusted for use. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::util::TlsConfigBuilder; + /// + /// let builder = TlsConfigBuilder::new().danger_accept_invalid_certs(true); + /// ``` + pub fn danger_accept_invalid_certs(mut self, is_invalid: bool) -> Self { + if is_invalid { + self.inner = self.inner.and_then(|mut builder| { + { + builder.set_verify(crate::util::c_openssl::ssl::SSL_VERIFY_NONE); + Ok(()) + } + .map(|_| builder) + }); + } + self + } + + /// Controls the use of hostname verification. + /// + /// Defaults to `false` -- verify hostname. + /// + /// # Warning + /// + /// When sets `true`, any valid certificate for any site will be trusted for + /// use from any other. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::util::TlsConfigBuilder; + /// + /// let builder = TlsConfigBuilder::new().danger_accept_invalid_hostnames(true); + /// ``` + pub fn danger_accept_invalid_hostnames(mut self, invalid_hostname: bool) -> Self { + self.verify_hostname = !invalid_hostname; + self + } + + /// Controls the use of TLS server name indication. + /// + /// Defaults to `true` -- sets sni. + /// + /// # Examples + /// + /// ``` + /// use ylong_http_client::util::TlsConfigBuilder; + /// + /// let builder = TlsConfigBuilder::new().sni(true); + /// ``` + pub fn sni(mut self, use_sni: bool) -> Self { + self.use_sni = use_sni; + self + } + /// Builds a `TlsContext`. Returns `Err` if an error occurred during configuration. /// /// # Examples @@ -271,16 +358,23 @@ impl TlsConfigBuilder { /// use ylong_http_client::util::{TlsConfigBuilder, TlsVersion}; /// /// let context = TlsConfigBuilder::new() - /// .set_ca_file("ca.crt") - /// .set_max_proto_version(TlsVersion::TLS_1_2) - /// .set_min_proto_version(TlsVersion::TLS_1_2) - /// .set_cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") + /// .ca_file("ca.crt") + /// .max_proto_version(TlsVersion::TLS_1_2) + /// .min_proto_version(TlsVersion::TLS_1_2) + /// .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") /// .build(); /// ``` pub fn build(self) -> Result { - Ok(TlsConfig(self.inner.build().map_err(|e| { - HttpClientError::new_with_cause(ErrorKind::Build, Some(e)) - })?)) + let ctx = self + .inner + .map(|builder| builder.build()) + .map_err(|e| HttpClientError::new_with_cause(ErrorKind::Build, Some(e)))?; + + Ok(TlsConfig { + ctx, + use_sni: self.use_sni, + verify_hostname: self.verify_hostname, + }) } } @@ -301,7 +395,11 @@ impl Default for TlsConfigBuilder { /// let builder = TlsConfig::builder(); /// ``` #[derive(Clone)] -pub struct TlsConfig(SslContext); +pub struct TlsConfig { + ctx: SslContext, + use_sni: bool, + verify_hostname: bool, +} impl TlsConfig { /// Creates a new, default `TlsContextBuilder`. @@ -318,16 +416,29 @@ impl TlsConfig { } /// Creates a new, default `TlsSsl`. - pub(crate) fn ssl(&self) -> Result { - let ctx = &self.0; - let ssl = Ssl::new(ctx)?; + pub(crate) fn ssl_new(&self, domain: &str) -> Result { + let ctx = &self.ctx; + let mut ssl = Ssl::new(ctx)?; + + // SNI extension in `ClientHello` stage. + if self.use_sni && domain.parse::().is_err() { + ssl.set_host_name_in_sni(domain)?; + } + + // Hostname verification in certificate verification. + if self.verify_hostname { + ssl.set_verify_hostname(domain)?; + } Ok(TlsSsl(ssl)) } } impl Default for TlsConfig { fn default() -> Self { - TlsConfig::builder().build().unwrap() + // It must can be successful. + TlsConfig::builder() + .build() + .expect("TlsConfig build error!") } } @@ -338,14 +449,6 @@ impl TlsSsl { pub(crate) fn into_inner(self) -> Ssl { self.0 } - - pub(crate) fn set_sni_verify(&mut self, name: &str) -> Result<(), ErrorStack> { - let ssl = &mut self.0; - if name.parse::().is_err() { - ssl.set_host_name(name)?; - } - Ok(()) - } } /// `TlsVersion` is based on `openssl::SslVersion`, which provides `SSL/TLS` @@ -524,7 +627,7 @@ mod ut_openssl_adapter { fn ut_tls_config_builder_new() { let _ = TlsConfigBuilder::default(); let builder = TlsConfigBuilder::new(); - assert!(builder.set_ca_file("folder/ca.crt").build().is_err()); + assert!(builder.ca_file("folder/ca.crt").build().is_err()); } /// UT test cases for `TlsConfigBuilder::new`. @@ -536,7 +639,7 @@ mod ut_openssl_adapter { /// 4. Checks if the result is as expected. #[test] fn ut_set_cipher_suites() { - let builder = TlsConfigBuilder::new().set_cipher_suites("INVALID STRING"); + let builder = TlsConfigBuilder::new().cipher_suites("INVALID STRING"); assert!(builder.build().is_err()); } @@ -549,7 +652,7 @@ mod ut_openssl_adapter { #[test] fn ut_set_max_proto_version() { let builder = TlsConfigBuilder::new() - .set_max_proto_version(TlsVersion::TLS_1_2) + .max_proto_version(TlsVersion::TLS_1_2) .build(); assert!(builder.is_ok()); } @@ -563,7 +666,7 @@ mod ut_openssl_adapter { #[test] fn ut_set_min_proto_version() { let builder = TlsConfigBuilder::new() - .set_min_proto_version(TlsVersion::TLS_1_2) + .min_proto_version(TlsVersion::TLS_1_2) .build(); assert!(builder.is_ok()); } @@ -577,9 +680,7 @@ mod ut_openssl_adapter { #[test] fn ut_set_cipher_list() { let builder = TlsConfigBuilder::new() - .set_cipher_list( - "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", - ) + .cipher_list("DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK") .build(); assert!(builder.is_ok()); } @@ -594,7 +695,7 @@ mod ut_openssl_adapter { #[test] fn ut_set_certificate_file() { let builder = TlsConfigBuilder::new() - .set_certificate_file("cert.pem", TlsFileType::PEM) + .certificate_file("cert.pem", TlsFileType::PEM) .build(); assert!(builder.is_err()); } @@ -609,7 +710,7 @@ mod ut_openssl_adapter { #[test] fn ut_set_certificate_chain_file() { let builder = TlsConfigBuilder::new() - .set_certificate_chain_file("cert.pem") + .certificate_chain_file("cert.pem") .build(); assert!(builder.is_err()); } @@ -654,7 +755,7 @@ mod ut_openssl_adapter { #[test] fn ut_set_alpn_proto_list() { let builder = TlsConfigBuilder::new() - .set_alpn_proto_list( + .alpn_proto_list( AlpnProtocolList::new() .extend(AlpnProtocol::HTTP11) .extend(AlpnProtocol::H2), @@ -667,18 +768,18 @@ mod ut_openssl_adapter { /// /// # Brief /// 1. Creates a `TlsConfig` by calling `TlsConfigBuilder::new` and `TlsConfigBuilder::build`. - /// 2. Creates a `TlsSsl` by calling `TlsConfig::ssl`. - /// 3. Calls `TlsSsl::set_sni_verify` and `TlsSsl::into_inner`. + /// 2. Creates a `TlsSsl` by calling `TlsConfig::ssl_new`. + /// 3. Calls `TlsSsl::into_inner`. /// 4. Checks if the result is as expected. #[test] fn ut_tls_ssl() { let config = TlsConfigBuilder::new() .build() .expect("TlsConfig build error."); - let mut ssl = config.ssl().expect("Ssl build error."); - ssl.set_sni_verify("host name") - .expect("Set SNI verify error."); - let _ssl = ssl.into_inner(); + let _ssl = config + .ssl_new("host name") + .expect("Ssl build error.") + .into_inner(); } /// UT test cases for `Cert::from_pem`. diff --git a/ylong_http_client/src/util/c_openssl/mod.rs b/ylong_http_client/src/util/c_openssl/mod.rs index 62a63de..5892ea7 100644 --- a/ylong_http_client/src/util/c_openssl/mod.rs +++ b/ylong_http_client/src/util/c_openssl/mod.rs @@ -28,19 +28,6 @@ pub(crate) mod x509; pub mod adapter; pub use adapter::{Cert, Certificate, TlsConfig, TlsConfigBuilder, TlsFileType, TlsVersion}; -// Copyright (c) 2023 Huawei Device Co., Ltd. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - use crate::util::c_openssl::ffi::OPENSSL_init_ssl; use core::ptr; use error::ErrorStack; diff --git a/ylong_http_client/src/util/c_openssl/ssl/ctx.rs b/ylong_http_client/src/util/c_openssl/ssl/ctx.rs index c984d35..dd263ed 100644 --- a/ylong_http_client/src/util/c_openssl/ssl/ctx.rs +++ b/ylong_http_client/src/util/c_openssl/ssl/ctx.rs @@ -51,7 +51,7 @@ foreign_type!( ); impl SslContext { - pub(crate) fn builder(method: SslMethod) -> SslContextBuilder { + pub(crate) fn builder(method: SslMethod) -> Result { SslContextBuilder::new(method) } } @@ -80,60 +80,47 @@ impl ToOwned for SslContextRef { } } -const SSL_VERIFY_PEER: c_int = 1; +pub(crate) const SSL_VERIFY_NONE: c_int = 0; +pub(crate) const SSL_VERIFY_PEER: c_int = 1; /// A builder for `SslContext`. -pub(crate) struct SslContextBuilder(Result); +pub(crate) struct SslContextBuilder(SslContext); impl SslContextBuilder { - pub(crate) fn new(method: SslMethod) -> Self { + pub(crate) fn new(method: SslMethod) -> Result { ssl_init(); - let ptr = match check_ptr(unsafe { SSL_CTX_new(method.as_ptr()) }) { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), - }; - if let Err(e) = check_ret(unsafe { SSL_CTX_set_default_verify_paths(ptr) }) { - return SslContextBuilder(Err(e)); - }; - unsafe { - SSL_CTX_set_verify(ptr, SSL_VERIFY_PEER, None); - } + let ptr = check_ptr(unsafe { SSL_CTX_new(method.as_ptr()) })?; + check_ret(unsafe { SSL_CTX_set_default_verify_paths(ptr) })?; - SslContextBuilder::from_ptr(ptr).set_cipher_list( + let mut builder = Self::from_ptr(ptr); + builder.set_verify(SSL_VERIFY_PEER); + builder.set_cipher_list( "DEFAULT:!aNULL:!eNULL:!MD5:!3DES:!DES:!RC4:!IDEA:!SEED:!aDSS:!SRP:!PSK", - ) + )?; + + Ok(builder) } /// Creates a `SslContextBuilder` from a `SSL_CTX`. pub(crate) fn from_ptr(ptr: *mut SSL_CTX) -> Self { - SslContextBuilder(Ok(SslContext(ptr))) + SslContextBuilder(SslContext(ptr)) } - /// Creates a `SslContextBuilder` from a `SSL_CTX`. - pub(crate) fn as_ptr(&self) -> Result<*mut SSL_CTX, ErrorStack> { - match &self.0 { - Ok(ctx) => Ok(ctx.0), - Err(e) => Err(e.to_owned()), - } + /// Creates a `*mut SSL_CTX` from a `SSL_CTX`. + pub(crate) fn as_ptr_mut(&mut self) -> *mut SSL_CTX { + self.0 .0 } /// Builds a `SslContext`. - pub(crate) fn build(self) -> Result { + pub(crate) fn build(self) -> SslContext { self.0 } - pub(crate) fn from_error(e: ErrorStack) -> Self { - SslContextBuilder(Err(e)) - } - - pub(crate) fn set_min_proto_version(self, version: SslVersion) -> Self { - let ptr = match self.as_ptr() { - Ok(p) => p, - Err(e) => return SslContextBuilder(Err(e)), - }; + pub(crate) fn set_min_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack> { + let ptr = self.as_ptr_mut(); - match check_ret(unsafe { + check_ret(unsafe { SSL_CTX_ctrl( ptr, SSL_CTRL_SET_MIN_PROTO_VERSION, @@ -141,19 +128,13 @@ impl SslContextBuilder { ptr::null_mut(), ) } as c_int) - { - Ok(_num) => self, - Err(e) => SslContextBuilder(Err(e)), - } + .map(|_| ()) } - pub(crate) fn set_max_proto_version(self, version: SslVersion) -> Self { - let ptr = match self.as_ptr() { - Ok(p) => p, - Err(e) => return SslContextBuilder(Err(e)), - }; + pub(crate) fn set_max_proto_version(&mut self, version: SslVersion) -> Result<(), ErrorStack> { + let ptr = self.as_ptr_mut(); - match check_ret(unsafe { + check_ret(unsafe { SSL_CTX_ctrl( ptr, SSL_CTRL_SET_MAX_PROTO_VERSION, @@ -161,73 +142,53 @@ impl SslContextBuilder { ptr::null_mut(), ) } as c_int) - { - Ok(_num) => self, - Err(e) => SslContextBuilder(Err(e)), - } + .map(|_| ()) } /// Loads trusted root certificates from a file.\ /// Uses to Set default locations for trusted CA certificates. /// /// The file should contain a sequence of PEM-formatted CA certificates. - pub(crate) fn set_ca_file

(self, file: P) -> Self + pub(crate) fn set_ca_file

(&mut self, file: P) -> Result<(), ErrorStack> where P: AsRef, { let path = match file.as_ref().as_os_str().to_str() { Some(path) => path, - None => return SslContextBuilder(Err(ErrorStack::get())), + None => return Err(ErrorStack::get()), }; let file = match CString::new(path) { Ok(path) => path, - Err(_) => return SslContextBuilder(Err(ErrorStack::get())), - }; - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), + Err(_) => return Err(ErrorStack::get()), }; + let ptr = self.as_ptr_mut(); - match check_ret(unsafe { + check_ret(unsafe { SSL_CTX_load_verify_locations(ptr, file.as_ptr() as *const _, ptr::null()) - }) { - Ok(_num) => self, - Err(e) => SslContextBuilder(Err(e)), - } + }) + .map(|_| ()) } /// Sets the list of supported ciphers for protocols before `TLSv1.3`. - pub(crate) fn set_cipher_list(self, list: &str) -> Self { + pub(crate) fn set_cipher_list(&mut self, list: &str) -> Result<(), ErrorStack> { let list = match CString::new(list) { Ok(cstr) => cstr, - Err(_) => return SslContextBuilder(Err(ErrorStack::get())), - }; - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), + Err(_) => return Err(ErrorStack::get()), }; + let ptr = self.as_ptr_mut(); - match check_ret(unsafe { SSL_CTX_set_cipher_list(ptr, list.as_ptr() as *const _) }) { - Ok(_num) => self, - Err(e) => SslContextBuilder(Err(e)), - } + check_ret(unsafe { SSL_CTX_set_cipher_list(ptr, list.as_ptr() as *const _) }).map(|_| ()) } /// Sets the list of supported ciphers for the `TLSv1.3` protocol. - pub(crate) fn set_cipher_suites(self, list: &str) -> Self { + pub(crate) fn set_cipher_suites(&mut self, list: &str) -> Result<(), ErrorStack> { let list = match CString::new(list) { Ok(cstr) => cstr, - Err(_) => return SslContextBuilder(Err(ErrorStack::get())), - }; - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), + Err(_) => return Err(ErrorStack::get()), }; + let ptr = self.as_ptr_mut(); - match check_ret(unsafe { SSL_CTX_set_ciphersuites(ptr, list.as_ptr() as *const _) }) { - Ok(_num) => self, - Err(e) => SslContextBuilder(Err(e)), - } + check_ret(unsafe { SSL_CTX_set_ciphersuites(ptr, list.as_ptr() as *const _) }).map(|_| ()) } /// Loads a leaf certificate from a file. @@ -235,29 +196,28 @@ impl SslContextBuilder { /// Only a single certificate will be loaded - use `add_extra_chain_cert` to add the remainder /// of the certificate chain, or `set_certificate_chain_file` to load the entire chain from a /// single file. - pub(crate) fn set_certificate_file

(self, file: P, file_type: SslFiletype) -> Self + pub(crate) fn set_certificate_file

( + &mut self, + file: P, + file_type: SslFiletype, + ) -> Result<(), ErrorStack> where P: AsRef, { let path = match file.as_ref().as_os_str().to_str() { Some(path) => path, - None => return SslContextBuilder(Err(ErrorStack::get())), + None => return Err(ErrorStack::get()), }; let file = match CString::new(path) { Ok(path) => path, - Err(_) => return SslContextBuilder(Err(ErrorStack::get())), - }; - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), + Err(_) => return Err(ErrorStack::get()), }; + let ptr = self.as_ptr_mut(); - match check_ret(unsafe { + check_ret(unsafe { SSL_CTX_use_certificate_file(ptr, file.as_ptr() as *const _, file_type.as_raw()) - }) { - Ok(_num) => self, - Err(e) => SslContextBuilder(Err(e)), - } + }) + .map(|_| ()) } /// Loads a certificate chain from file into ctx. @@ -265,64 +225,51 @@ impl SslContextBuilder { /// the subject's certificate (actual client or server certificate), followed /// by intermediate CA certificates if applicable, and ending at the highest /// level (root) CA. - pub(crate) fn set_certificate_chain_file

(self, file: P) -> Self + pub(crate) fn set_certificate_chain_file

(&mut self, file: P) -> Result<(), ErrorStack> where P: AsRef, { let path = match file.as_ref().as_os_str().to_str() { Some(path) => path, - None => return SslContextBuilder(Err(ErrorStack::get())), + None => return Err(ErrorStack::get()), }; let file = match CString::new(path) { Ok(path) => path, - Err(_) => return SslContextBuilder(Err(ErrorStack::get())), - }; - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), + Err(_) => return Err(ErrorStack::get()), }; + let ptr = self.as_ptr_mut(); - match check_ret(unsafe { - SSL_CTX_use_certificate_chain_file(ptr, file.as_ptr() as *const _) - }) { - Ok(_num) => self, - Err(e) => SslContextBuilder(Err(e)), - } + check_ret(unsafe { SSL_CTX_use_certificate_chain_file(ptr, file.as_ptr() as *const _) }) + .map(|_| ()) } /// Sets the protocols to sent to the server for Application Layer Protocol Negotiation (ALPN). - pub(crate) fn set_alpn_protos(self, protocols: &[u8]) -> Self { + pub(crate) fn set_alpn_protos(&mut self, protocols: &[u8]) -> Result<(), ErrorStack> { assert!(protocols.len() <= c_uint::max_value() as usize); - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), - }; - + let ptr = self.as_ptr_mut(); match unsafe { SSL_CTX_set_alpn_protos(ptr, protocols.as_ptr(), protocols.len() as c_uint) } { - 0 => self, - _ => SslContextBuilder(Err(ErrorStack::get())), + 0 => Ok(()), + _ => Err(ErrorStack::get()), } } - pub(crate) fn set_cert_store(self, cert_store: X509Store) -> Self { - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return SslContextBuilder(Err(e)), - }; + pub(crate) fn set_verify(&mut self, mode: c_int) { + let ptr = self.as_ptr_mut(); + unsafe { SSL_CTX_set_verify(ptr, mode, None) }; + } + + pub(crate) fn set_cert_store(&mut self, cert_store: X509Store) { + let ptr = self.as_ptr_mut(); unsafe { SSL_CTX_set_cert_store(ptr, cert_store.as_ptr()); mem::forget(cert_store); } - self } - pub(crate) fn cert_store_mut(&mut self) -> Result<&mut X509StoreRef, ErrorStack> { - let ptr = match self.as_ptr() { - Ok(ptr) => ptr, - Err(e) => return Err(e), - }; - Ok(unsafe { X509StoreRef::from_ptr_mut(SSL_CTX_get_cert_store(ptr)) }) + pub(crate) fn cert_store_mut(&mut self) -> &mut X509StoreRef { + let ptr = self.as_ptr_mut(); + unsafe { X509StoreRef::from_ptr_mut(SSL_CTX_get_cert_store(ptr)) } } } diff --git a/ylong_http_client/src/util/c_openssl/ssl/mod.rs b/ylong_http_client/src/util/c_openssl/ssl/mod.rs index 8ea9e7e..3559b6d 100644 --- a/ylong_http_client/src/util/c_openssl/ssl/mod.rs +++ b/ylong_http_client/src/util/c_openssl/ssl/mod.rs @@ -19,7 +19,7 @@ mod ssl_base; mod stream; mod version; -pub(crate) use ctx::{SslContext, SslContextBuilder}; +pub(crate) use ctx::{SslContext, SslContextBuilder, SSL_VERIFY_NONE, SSL_VERIFY_PEER}; pub(crate) use error::{InternalError, SslError, SslErrorCode}; pub(crate) use filetype::SslFiletype; pub(crate) use method::SslMethod; diff --git a/ylong_http_client/src/util/c_openssl/ssl/ssl_base.rs b/ylong_http_client/src/util/c_openssl/ssl/ssl_base.rs index 4ce3567..5120500 100644 --- a/ylong_http_client/src/util/c_openssl/ssl/ssl_base.rs +++ b/ylong_http_client/src/util/c_openssl/ssl/ssl_base.rs @@ -115,7 +115,7 @@ impl SslRef { unsafe { SSL_write(self.as_ptr(), buf.as_ptr() as *const c_void, len) } } - pub(crate) fn set_host_name(&mut self, name: &str) -> Result<(), ErrorStack> { + pub(crate) fn set_host_name_in_sni(&mut self, name: &str) -> Result<(), ErrorStack> { let name = match CString::new(name) { Ok(name) => name, Err(_) => return Err(ErrorStack::get()), @@ -130,12 +130,12 @@ impl SslRef { unsafe { X509VerifyParamRef::from_ptr_mut(SSL_get0_param(self.as_ptr())) } } - pub(crate) fn setup_verify_hostname(ssl: &mut SslRef, host: &str) -> Result<(), ErrorStack> { - let param = ssl.param_mut(); + pub(crate) fn set_verify_hostname(&mut self, host_name: &str) -> Result<(), ErrorStack> { + let param = self.param_mut(); param.set_hostflags(X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); - match host.parse() { + match host_name.parse() { Ok(ip) => param.set_ip(ip), - Err(_) => param.set_host(host), + Err(_) => param.set_host(host_name), } } } diff --git a/ylong_http_client/tests/common/async_utils.rs b/ylong_http_client/tests/common/async_utils.rs index 206f921..6381af1 100644 --- a/ylong_http_client/tests/common/async_utils.rs +++ b/ylong_http_client/tests/common/async_utils.rs @@ -377,7 +377,8 @@ macro_rules! async_client_assertions { #[cfg(feature = "__tls")] pub fn async_build_https_client(tls_config: &str) -> Arc { let client = ylong_http_client::async_impl::Client::builder() - .set_ca_file(tls_config) + .tls_ca_file(tls_config) + .danger_accept_invalid_hostnames(true) // The root-ca is not have SAN hostname. .build() .unwrap(); std::sync::Arc::new(client) diff --git a/ylong_http_client/tests/common/sync_utils.rs b/ylong_http_client/tests/common/sync_utils.rs index 5af4264..cbedb9c 100644 --- a/ylong_http_client/tests/common/sync_utils.rs +++ b/ylong_http_client/tests/common/sync_utils.rs @@ -219,7 +219,8 @@ macro_rules! sync_client_assert { },)* ) => {{ let client = ylong_http_client::sync_impl::Client::builder() - .set_ca_file($tls_config) + .tls_ca_file($tls_config) + .danger_accept_invalid_hostnames(true) .build() .unwrap(); let client = std::sync::Arc::new(client); -- Gitee