From 9ab8ab945cac25d84f6f5d3987c8ffebdba188a0 Mon Sep 17 00:00:00 2001 From: Nick Lanham Date: Fri, 21 Feb 2020 11:39:31 -0800 Subject: [PATCH] add ability to create a client with own tls connector (#809) --- src/async_impl/client.rs | 62 ++++++++++++++++++++++++++++++++++++++-- src/blocking/client.rs | 10 +++++++ src/connect.rs | 25 ++++++++++++++++ src/tls.rs | 22 +++++++++++++- tests/client.rs | 39 +++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 0552537..5cf3546 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "__tls")] +use std::any::Any; use std::convert::TryInto; use std::net::IpAddr; use std::sync::Arc; @@ -188,6 +190,7 @@ impl ClientBuilder { } } + Connector::new_default_tls( tls, proxies.clone(), @@ -195,7 +198,25 @@ impl ClientBuilder { config.local_address, config.nodelay, )? - } + }, + #[cfg(feature = "default-tls")] + TlsBackend::BuiltDefault(conn) => { + Connector::from_built_default( + conn, + proxies.clone(), + user_agent(&config.headers), + config.local_address, + config.nodelay)? + }, + #[cfg(feature = "rustls-tls")] + TlsBackend::BuiltRustls(conn) => { + Connector::new_rustls_tls( + conn, + proxies.clone(), + user_agent(&config.headers), + config.local_address, + config.nodelay)? + }, #[cfg(feature = "rustls-tls")] TlsBackend::Rustls => { use crate::tls::NoVerifier; @@ -229,7 +250,12 @@ impl ClientBuilder { config.local_address, config.nodelay, )? - } + }, + TlsBackend::UnknownPreconfigured => { + return Err(crate::error::builder( + "Unknown TLS backend passed to `use_preconfigured_tls`" + )); + }, } #[cfg(not(feature = "__tls"))] @@ -711,6 +737,38 @@ impl ClientBuilder { self.config.tls = TlsBackend::Rustls; self } + + /// Use a preconfigured TLS backend. + /// + /// If the passed `Any` argument is not a TLS backend that reqwest + /// understands, the `ClientBuilder` will error when calling `build`. + #[cfg(feature = "__tls")] + pub fn use_preconfigured_tls(mut self, tls: impl Any) -> ClientBuilder { + let mut tls = Some(tls); + #[cfg(feature = "default-tls")] + { + if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::>() { + let tls = conn.take().expect("is definitely Some"); + let tls = crate::tls::TlsBackend::BuiltDefault(tls); + self.config.tls = tls; + return self; + } + } + #[cfg(feature = "rustls-tls")] + { + if let Some(conn) = (&mut tls as &mut dyn Any).downcast_mut::>() { + + let tls = conn.take().expect("is definitely Some"); + let tls = crate::tls::TlsBackend::BuiltRustls(tls); + self.config.tls = tls; + return self; + } + } + + // Otherwise, we don't recognize the TLS backend! + self.config.tls = crate::tls::TlsBackend::UnknownPreconfigured; + self + } } type HyperClient = hyper::Client; diff --git a/src/blocking/client.rs b/src/blocking/client.rs index a3f7ce3..abe9606 100644 --- a/src/blocking/client.rs +++ b/src/blocking/client.rs @@ -1,3 +1,4 @@ +use std::any::Any; use std::convert::TryInto; use std::fmt; use std::future::Future; @@ -450,6 +451,15 @@ impl ClientBuilder { self.with_inner(move |inner| inner.use_rustls_tls()) } + /// Use a preconfigured TLS backend. + /// + /// If the passed `Any` argument is not a TLS backend that reqwest + /// understands, the `ClientBuilder` will error when calling `build`. + #[cfg(feature = "__tls")] + pub fn use_preconfigured_tls(self, tls: impl Any) -> ClientBuilder { + self.with_inner(move |inner| inner.use_preconfigured_tls(tls)) + } + // private fn with_inner(mut self, func: F) -> ClientBuilder diff --git a/src/connect.rs b/src/connect.rs index 96e4733..6109e45 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -107,6 +107,31 @@ impl Connector { }) } + #[cfg(feature = "default-tls")] + pub(crate) fn from_built_default ( + tls: TlsConnector, + proxies: Arc>, + user_agent: Option, + local_addr: T, + nodelay: bool) -> crate::Result + where + T: Into>, + { + + let mut http = http_connector()?; + http.set_local_address(local_addr.into()); + http.enforce_http(false); + + Ok(Connector { + inner: Inner::DefaultTls(http, tls), + proxies, + verbose: verbose::OFF, + timeout: None, + nodelay, + user_agent, + }) + } + #[cfg(feature = "rustls-tls")] pub(crate) fn new_rustls_tls( tls: rustls::ClientConfig, diff --git a/src/tls.rs b/src/tls.rs index fa57acc..cdcbe9c 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -266,12 +266,32 @@ impl fmt::Debug for Identity { } } -#[derive(Debug)] pub(crate) enum TlsBackend { #[cfg(feature = "default-tls")] Default, + #[cfg(feature = "default-tls")] + BuiltDefault(native_tls_crate::TlsConnector), #[cfg(feature = "rustls-tls")] Rustls, + #[cfg(feature = "rustls-tls")] + BuiltRustls(rustls::ClientConfig), + UnknownPreconfigured, +} + +impl fmt::Debug for TlsBackend { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + #[cfg(feature = "default-tls")] + TlsBackend::Default => write!(f, "Default"), + #[cfg(feature = "default-tls")] + TlsBackend::BuiltDefault(_) => write!(f, "BuiltDefault"), + #[cfg(feature = "rustls-tls")] + TlsBackend::Rustls => write!(f, "Rustls"), + #[cfg(feature = "rustls-tls")] + TlsBackend::BuiltRustls(_) => write!(f, "BuiltRustls"), + TlsBackend::UnknownPreconfigured => write!(f, "UnknownPreconfigured"), + } + } } impl Default for TlsBackend { diff --git a/tests/client.rs b/tests/client.rs index 478bc9b..7ac73a4 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -133,3 +133,42 @@ async fn body_pipe_response() { assert_eq!(res2.status(), reqwest::StatusCode::OK); } + +#[cfg(feature = "__tls")] +#[test] +fn use_preconfigured_tls_with_bogus_backend() { + struct DefinitelyNotTls; + + reqwest::Client::builder() + .use_preconfigured_tls(DefinitelyNotTls) + .build() + .expect_err("definitely is not TLS"); +} + +#[cfg(feature = "default-tls")] +#[test] +fn use_preconfigured_native_tls_default() { + extern crate native_tls_crate; + + let tls = native_tls_crate::TlsConnector::builder() + .build() + .expect("tls builder"); + + reqwest::Client::builder() + .use_preconfigured_tls(tls) + .build() + .expect("preconfigured default tls"); +} + +#[cfg(feature = "rustls-tls")] +#[test] +fn use_preconfigured_rustls_default() { + extern crate rustls; + + let tls = rustls::ClientConfig::new(); + + reqwest::Client::builder() + .use_preconfigured_tls(tls) + .build() + .expect("preconfigured rustls tls"); +}