From a70b2b083613a406598165f1af56727ffa238506 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:04:29 -0500 Subject: [PATCH] refactor(s2n-tls-hyper): Add HttpsConnector builder (#4976) --- bindings/rust/s2n-tls-hyper/src/connector.rs | 102 ++++++++++++++----- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index ea5670d62f0..8721bff70ee 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -24,49 +24,64 @@ use tower_service::Service; /// which sends and receives requests over TCP. The `HttpsConnector` struct wraps an HTTP connector, /// and uses it to negotiate TLS when the HTTPS scheme is in use. #[derive(Clone)] -pub struct HttpsConnector { +pub struct HttpsConnector { http: Http, - conn_builder: Builder, + conn_builder: ConnBuilder, } -impl HttpsConnector +impl HttpsConnector where - Builder: connection::Builder, - ::Output: Unpin, + ConnBuilder: connection::Builder, + ::Output: Unpin, { - /// Creates a new `HttpsConnector`. + /// Creates a new `HttpsConnector` with the default settings. + /// + /// Use `HttpsConnector::builder()` instead to configure an `HttpsConnector`. + /// + /// `conn_builder` will be used to produce the s2n-tls Connections used for negotiating HTTPS, + /// which can be an `s2n_tls::config::Config` or other `s2n_tls::connection::Builder`. + /// + /// Note that s2n-tls-hyper will override the ALPN extension to negotiate HTTP. Any ALPN values + /// configured on `conn_builder` with APIs like + /// `s2n_tls::config::Builder::set_application_protocol_preference()` will be ignored. + pub fn new(conn_builder: ConnBuilder) -> HttpsConnector { + HttpsConnector::builder(conn_builder).build() + } + + /// Creates a `Builder` to configure a new `HttpsConnector`. /// /// `conn_builder` will be used to produce the s2n-tls Connections used for negotiating HTTPS, /// which can be an `s2n_tls::config::Config` or other `s2n_tls::connection::Builder`. /// - /// This API creates an `HttpsConnector` using the default hyper `HttpConnector`. To use an - /// existing HTTP connector, use `HttpsConnector::new_with_http()`. + /// This API creates a `Builder` with the default hyper `HttpConnector`. To use an existing HTTP + /// connector, use `HttpsConnector::builder_with_http()`. /// /// Note that s2n-tls-hyper will override the ALPN extension to negotiate HTTP. Any ALPN values /// configured on `conn_builder` with APIs like /// `s2n_tls::config::Builder::set_application_protocol_preference()` will be ignored. - pub fn new(conn_builder: Builder) -> HttpsConnector { + pub fn builder(conn_builder: ConnBuilder) -> Builder { let mut http = HttpConnector::new(); // By default, the `HttpConnector` only allows the HTTP URI scheme to be used. To negotiate // HTTP over TLS via the HTTPS scheme, `enforce_http` must be disabled. http.enforce_http(false); - Self { http, conn_builder } + HttpsConnector::builder_with_http(http, conn_builder) } } -impl HttpsConnector +impl HttpsConnector where - Builder: connection::Builder, - ::Output: Unpin, + ConnBuilder: connection::Builder, + ::Output: Unpin, { - /// Creates a new `HttpsConnector`. + /// Creates a `Builder` to configure a new `HttpsConnector`. /// /// `conn_builder` will be used to produce the s2n-tls Connections used for negotiating HTTPS, /// which can be an `s2n_tls::config::Config` or other `s2n_tls::connection::Builder`. /// - /// This API allows an `HttpsConnector` to be constructed with an existing HTTP connector, as follows: + /// This API allows an `HttpsConnector` to be constructed with an existing HTTP connector, as + /// follows: /// ``` /// use s2n_tls_hyper::connector::HttpsConnector; /// use s2n_tls::config::Config; @@ -77,16 +92,33 @@ where /// // Ensure that the HTTP connector permits the HTTPS scheme. /// http.enforce_http(false); /// - /// let connector = HttpsConnector::new_with_http(http, Config::default()); + /// let connector = HttpsConnector::builder_with_http(http, Config::default()).build(); /// ``` /// - /// `HttpsConnector::new()` can be used to create the HTTP connector automatically. + /// `HttpsConnector::builder()` can be used to create the HTTP connector automatically. /// /// Note that s2n-tls-hyper will override the ALPN extension to negotiate HTTP. Any ALPN values /// configured on `conn_builder` with APIs like /// `s2n_tls::config::Builder::set_application_protocol_preference()` will be ignored. - pub fn new_with_http(http: Http, conn_builder: Builder) -> HttpsConnector { - Self { http, conn_builder } + pub fn builder_with_http(http: Http, conn_builder: ConnBuilder) -> Builder { + Builder { http, conn_builder } + } +} + +/// Builder used to configure an `HttpsConnector`. Create a new Builder with +/// `HttpsConnector::builder`. +pub struct Builder { + http: Http, + conn_builder: ConnBuilder, +} + +impl Builder { + /// Builds a new `HttpsConnector`. + pub fn build(self) -> HttpsConnector { + HttpsConnector { + http: self.http, + conn_builder: self.conn_builder, + } } } @@ -96,19 +128,22 @@ where // https://docs.rs/hyper-util/latest/hyper_util/client/legacy/connect/trait.Connect.html // // The hyper compatibility traits for `Service::Response` are implemented in `MaybeHttpsStream`. -impl Service for HttpsConnector +impl Service for HttpsConnector where Http: Service, Http::Response: Read + Write + Connection + Unpin + Send + 'static, Http::Future: Send + 'static, Http::Error: Into>, - Builder: connection::Builder + Send + Sync + 'static, - ::Output: Unpin + Send, + ConnBuilder: connection::Builder + Send + Sync + 'static, + ::Output: Unpin + Send, { - type Response = MaybeHttpsStream; + type Response = MaybeHttpsStream; type Error = Error; type Future = Pin< - Box, Error>> + Send>, + Box< + dyn Future, Error>> + + Send, + >, >; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { @@ -180,6 +215,25 @@ mod tests { use hyper_util::{client::legacy::Client, rt::TokioExecutor}; use std::{error::Error as StdError, str::FromStr}; + #[tokio::test] + async fn connector_creation() { + let config = Config::default(); + let connector_from_new = HttpsConnector::new(config.clone()); + let _assert_type: HttpsConnector = connector_from_new; + + let connector_from_builder = HttpsConnector::builder(config.clone()).build(); + let _assert_type: HttpsConnector = connector_from_builder; + + let http: u32 = 10; + let connector_from_builder_with_http = + HttpsConnector::builder_with_http(http, config.clone()).build(); + let _assert_type: HttpsConnector = connector_from_builder_with_http; + + let builder = HttpsConnector::builder(config.clone()); + let connector_from_builder = builder.build(); + let _assert_type: HttpsConnector = connector_from_builder; + } + #[tokio::test] async fn test_unsecure_http() -> Result<(), Box> { let connector = HttpsConnector::new(Config::default());