From 30a9202310850df765673ae4501e5c8504fe6fc4 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Wed, 11 Dec 2024 20:41:48 -0500 Subject: [PATCH] add insecure http option --- bindings/rust/s2n-tls-hyper/src/connector.rs | 38 ++++++++++++++---- bindings/rust/s2n-tls-hyper/tests/http.rs | 41 +++++++++++++++----- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index ac4b64a7e3a..76bc864dfc1 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -27,6 +27,7 @@ use tower_service::Service; pub struct HttpsConnector { http: Http, conn_builder: ConnBuilder, + insecure_http: bool, } impl HttpsConnector @@ -101,7 +102,11 @@ where http: HttpConnector, conn_builder: ConnBuilder, ) -> Builder { - Builder { http, conn_builder } + Builder { + http, + conn_builder, + insecure_http: false, + } } } @@ -110,14 +115,22 @@ where pub struct Builder { http: Http, conn_builder: ConnBuilder, + insecure_http: bool, } impl Builder { + /// Allows communication with insecure HTTP endpoints in addition to secure HTTPS endpoints. + pub fn with_insecure_http(&mut self) -> &mut Self { + self.insecure_http = true; + self + } + /// Builds a new `HttpsConnector`. pub fn build(self) -> HttpsConnector { HttpsConnector { http: self.http, conn_builder: self.conn_builder, + insecure_http: self.insecure_http, } } } @@ -155,10 +168,18 @@ where } fn call(&mut self, req: Uri) -> Self::Future { - // Currently, the only supported stream type is TLS. If the application attempts to - // negotiate HTTP over plain TCP, return an error. - if req.scheme() == Some(&http::uri::Scheme::HTTP) { - return Box::pin(async move { Err(Error::InvalidScheme) }); + match req.scheme() { + Some(scheme) if scheme == &http::uri::Scheme::HTTPS => (), + Some(scheme) if scheme == &http::uri::Scheme::HTTP && self.insecure_http => { + let call = self.http.call(req); + return Box::pin(async move { + let tcp = call.await.map_err(|e| Error::HttpError(e.into()))?; + Ok(MaybeHttpsStream::Http(tcp)) + }); + } + _ => { + return Box::pin(async move { Err(Error::InvalidScheme) }); + } } // Attempt to negotiate HTTP/2 by including it in the ALPN extension. Other supported HTTP @@ -235,15 +256,16 @@ mod tests { } #[tokio::test] - async fn test_unsecure_http() -> Result<(), Box> { + async fn test_invalid_scheme() -> Result<(), Box> { let connector = HttpsConnector::new(Config::default()); let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(connector); - let uri = Uri::from_str("http://www.amazon.com")?; + // Attempt to make a request with an arbitrary invalid scheme. + let uri = Uri::from_str("notascheme://www.amazon.com")?; let error = client.get(uri).await.unwrap_err(); - // Ensure that an InvalidScheme error is returned when HTTP over TCP is attempted. + // Ensure that an InvalidScheme error is returned. let error = error.source().unwrap().downcast_ref::().unwrap(); assert!(matches!(error, Error::InvalidScheme)); diff --git a/bindings/rust/s2n-tls-hyper/tests/http.rs b/bindings/rust/s2n-tls-hyper/tests/http.rs index b60ee0f8196..c18c5ccb430 100644 --- a/bindings/rust/s2n-tls-hyper/tests/http.rs +++ b/bindings/rust/s2n-tls-hyper/tests/http.rs @@ -17,6 +17,7 @@ use s2n_tls::{ security::DEFAULT_TLS13, }; use s2n_tls_hyper::connector::HttpsConnector; +use s2n_tls_hyper::error; use std::{error::Error, pin::Pin, str::FromStr}; use tokio::{ net::TcpListener, @@ -356,16 +357,38 @@ async fn insecure_http() -> Result<(), Box> { tasks.spawn(async move { for enable_insecure_http in [false, true] { - let mut builder = HttpsConnector::builder(); - } - - let connector = HttpsConnector::new(common::config()?.build()?); - let client: Client<_, Empty> = - Client::builder(TokioExecutor::new()).build(connector); + let connector = { + let config = common::config()?.build()?; + let mut builder = HttpsConnector::builder(config); + if enable_insecure_http { + builder.with_insecure_http(); + } + builder.build() + }; - let uri = Uri::from_str(format!("http://127.0.0.1:{}", addr.port()).as_str())?; - let response = client.get(uri).await?; - assert_eq!(response.status(), 200); + let client: Client<_, Empty> = + Client::builder(TokioExecutor::new()).build(connector); + let uri = Uri::from_str(format!("http://127.0.0.1:{}", addr.port()).as_str())?; + let response = client.get(uri).await; + + if enable_insecure_http { + // If insecure HTTP is enabled, the request should succeed. + let response = response.unwrap(); + assert_eq!(response.status(), 200); + } else { + // By default, insecure HTTP is disabled, and the request should error. + let error = response.unwrap_err(); + + // Ensure an InvalidScheme error is produced. + let error = error + .source() + .unwrap() + .downcast_ref::() + .unwrap(); + assert!(matches!(error, error::Error::InvalidScheme)); + assert!(!error.to_string().is_empty()); + } + } Ok(()) });