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..66ffc82 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -30,6 +30,36 @@ 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. +/// +/// 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 { let mut config = ClientConfig::new(); config.root_store = match rustls_native_certs::load_native_certs() { @@ -43,10 +73,17 @@ impl HttpsConnector { if config.root_store.is_empty() { panic!("no CA certificates found"); } - Self::build(config) + Self { tls_config: config } } - /// Construct a new `HttpsConnector` using the `webpki_roots` + /// 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 } + } + + /// 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 +91,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;