From 7f0d32b68b963f8b19f1641db00735d8d9f22c85 Mon Sep 17 00:00:00 2001 From: Gabriel de Perthuis Date: Thu, 11 Feb 2021 12:49:31 +0100 Subject: [PATCH 1/2] Add HttpsConnectorBuilder This gives more control over various rustls features, as well as ensures that enabling connector features like http2 can only be done when the appropriate crate features are enabled. Adds an hyper-rustls/http2 feature. --- Cargo.toml | 3 ++- examples/client.rs | 2 +- src/connector.rs | 59 ++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 4 ++-- 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 73f1643..707af82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,9 +28,10 @@ futures-util = { version = "0.3.1", default-features = false } [features] default = ["native-tokio"] +http2 = ["hyper/http2"] webpki-tokio = ["tokio-runtime", "webpki-roots"] native-tokio = ["tokio-runtime", "rustls-native-certs"] -tokio-runtime = ["hyper/runtime", "ct-logs"] +tokio-runtime = ["hyper/tcp", "ct-logs"] [[example]] name = "client" diff --git a/examples/client.rs b/examples/client.rs index 4e97d9d..a9fc74f 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -55,7 +55,7 @@ async fn run_client() -> io::Result<()> { hyper_rustls::HttpsConnector::from((http, tls)) } // Default HTTPS connector. - None => hyper_rustls::HttpsConnector::with_native_roots(), + None => hyper_rustls::HttpsConnectorBuilder::with_native_roots().build(), }; // Build the hyper client from the HTTPS connector. diff --git a/src/connector.rs b/src/connector.rs index 76dbf52..d428455 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -22,12 +22,18 @@ pub struct HttpsConnector { tls_config: Arc, } -#[cfg(all( - any(feature = "rustls-native-certs", feature = "webpki-roots"), - feature = "tokio-runtime" -))] -impl HttpsConnector { - /// Construct a new `HttpsConnector` using the OS root store +/// A builder that will configure an `HttpsConnector` +/// +/// This builder ensures configuration is consistent. +/// +/// An alternative way of building an `HttpsConnector` +/// is to use From/Into. +pub struct HttpsConnectorBuilder { + tls_config: ClientConfig, +} + +impl HttpsConnectorBuilder { + /// Configure using the OS root store for certificate trust #[cfg(feature = "rustls-native-certs")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] pub fn with_native_roots() -> Self { @@ -43,10 +49,17 @@ impl HttpsConnector { if config.root_store.is_empty() { panic!("no CA certificates found"); } - Self::build(config) + Self { tls_config: config } + } + + /// Configure using a custom `rustls::RootCertStore` for certificate trust + pub fn with_custom_roots(roots: rustls::RootCertStore) -> Self { + let mut config = ClientConfig::new(); + config.root_store = roots; + Self { tls_config: config } } - /// Construct a new `HttpsConnector` using the `webpki_roots` + /// Configure using `webpki_roots` for certificate trust #[cfg(feature = "webpki-roots")] #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] pub fn with_webpki_roots() -> Self { @@ -54,16 +67,36 @@ impl HttpsConnector { config .root_store .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - Self::build(config) + Self { tls_config: config } } - fn build(mut config: ClientConfig) -> Self { + /// Enable HTTP2 + /// This advertises http2 support in ALPN + #[cfg(feature = "http2")] + #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] + pub fn enable_http2(mut self) -> Self { + self.tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; + self + } + + /// Enable certificate transparency + #[cfg(feature = "ct-logs")] + pub fn enable_cert_transparency(mut self) -> Self { + self.tls_config.ct_logs = Some(&ct_logs::LOGS); + self + } + + /// Built an HttpsConnector + #[cfg(feature = "tokio-runtime")] + pub fn build(self) -> HttpsConnector { let mut http = HttpConnector::new(); http.enforce_http(false); + self.wrap_connector(http) + } - config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - config.ct_logs = Some(&ct_logs::LOGS); - (http, config).into() + /// Built an HttpsConnector with a custom lower-level connector + pub fn wrap_connector(self, conn: H) -> HttpsConnector { + (conn, self.tls_config).into() } } diff --git a/src/lib.rs b/src/lib.rs index 5374c06..8f0603f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ //! //! let mut rt = tokio::runtime::Runtime::new().unwrap(); //! let url = ("https://hyper.rs").parse().unwrap(); -//! let https = hyper_rustls::HttpsConnector::with_native_roots(); +//! let https = hyper_rustls::HttpsConnectorBuilder::with_native_roots().build(); //! //! let client: Client<_, hyper::Body> = Client::builder().build(https); //! @@ -27,5 +27,5 @@ mod connector; mod stream; -pub use crate::connector::HttpsConnector; +pub use crate::connector::{HttpsConnector, HttpsConnectorBuilder}; pub use crate::stream::MaybeHttpsStream; From a692d235931c91009ed8c39a2267fd1df630c487 Mon Sep 17 00:00:00 2001 From: Gabriel de Perthuis Date: Thu, 11 Feb 2021 13:08:13 +0100 Subject: [PATCH 2/2] Keep compatibility with existing build functions These functions implicitly enabled certificate transparency, keep that as well. --- src/connector.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/connector.rs b/src/connector.rs index d428455..66ffc82 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -22,6 +22,30 @@ pub struct HttpsConnector { tls_config: Arc, } +#[cfg(all( + any(feature = "rustls-native-certs", feature = "webpki-roots"), + feature = "tokio-runtime" +))] +impl HttpsConnector { + /// Construct a new `HttpsConnector` using the OS root store + #[cfg(feature = "rustls-native-certs")] + #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] + pub fn with_native_roots() -> Self { + HttpsConnectorBuilder::with_native_roots() + .enable_cert_transparency() + .build() + } + + /// Construct a new `HttpsConnector` using the `webpki_roots` + #[cfg(feature = "webpki-roots")] + #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] + pub fn with_webpki_roots() -> Self { + HttpsConnectorBuilder::with_webpki_roots() + .enable_cert_transparency() + .build() + } +} + /// A builder that will configure an `HttpsConnector` /// /// This builder ensures configuration is consistent.