From 977ca2b0ff98b550abbce934932368f288e3aec1 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:05:14 -0400 Subject: [PATCH 1/9] feat(bindings): Add hyper compatibility crate Co-authored-by: James Mayclin --- bindings/rust/Cargo.toml | 1 + bindings/rust/s2n-tls-hyper/Cargo.toml | 25 +++ bindings/rust/s2n-tls-hyper/README.md | 3 + bindings/rust/s2n-tls-hyper/src/connector.rs | 180 +++++++++++++++++++ bindings/rust/s2n-tls-hyper/src/lib.rs | 5 + bindings/rust/s2n-tls-hyper/src/stream.rs | 83 +++++++++ 6 files changed, 297 insertions(+) create mode 100644 bindings/rust/s2n-tls-hyper/Cargo.toml create mode 100644 bindings/rust/s2n-tls-hyper/README.md create mode 100644 bindings/rust/s2n-tls-hyper/src/connector.rs create mode 100644 bindings/rust/s2n-tls-hyper/src/lib.rs create mode 100644 bindings/rust/s2n-tls-hyper/src/stream.rs diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 5b82a43e898..15b00cc7009 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -4,6 +4,7 @@ members = [ "s2n-tls", "s2n-tls-sys", "s2n-tls-tokio", + "s2n-tls-hyper", ] # generate can't be included in the workspace because of a bootstrapping problem # s2n-tls-sys/Cargo.toml (part of the workspace) is generated by diff --git a/bindings/rust/s2n-tls-hyper/Cargo.toml b/bindings/rust/s2n-tls-hyper/Cargo.toml new file mode 100644 index 00000000000..a8321bc31a7 --- /dev/null +++ b/bindings/rust/s2n-tls-hyper/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "s2n-tls-hyper" +description = "A compatbility crate allowing s2n-tls to be used with the hyper HTTP library" +version = "0.0.1" +authors = ["AWS s2n"] +edition = "2021" +rust-version = "1.63.0" +repository = "https://github.com/aws/s2n-tls" +license = "Apache-2.0" + +[features] +default = [] + +[dependencies] +s2n-tls = { version = "=0.2.7", path = "../s2n-tls" } +s2n-tls-tokio = { version = "=0.2.7", path = "../s2n-tls-tokio" } +hyper = { version = "1" } +hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1"] } +tower-service = { version = "0.3" } +http = { version= "1" } + +[dev-dependencies] +tokio = { version = "1", features = ["macros", "test-util"] } +http-body-util = "0.1" +bytes = "1" diff --git a/bindings/rust/s2n-tls-hyper/README.md b/bindings/rust/s2n-tls-hyper/README.md new file mode 100644 index 00000000000..5e612865135 --- /dev/null +++ b/bindings/rust/s2n-tls-hyper/README.md @@ -0,0 +1,3 @@ +`s2n-tls-hyper` provides compatibility structs for [hyper](https://hyper.rs/), allowing s2n-tls to be used as the underlying TLS implementation with hyper clients. + +This crate is currently under development and is extremely unstable. diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs new file mode 100644 index 00000000000..e1a69d6dfb4 --- /dev/null +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -0,0 +1,180 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use crate::stream::MaybeHttpsStream; +use http::uri::Uri; +use hyper::rt::{Read, Write}; +use hyper_util::{ + client::legacy::connect::{Connection, HttpConnector}, + rt::TokioIo, +}; +use s2n_tls::{config::Config, connection}; +use s2n_tls_tokio::TlsConnector; +use std::{ + fmt, + fmt::{Debug, Formatter}, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; +use tower_service::Service; + +type BoxError = Box; + +#[derive(Clone)] +pub struct HttpsConnector +where + B: connection::Builder, + ::Output: Unpin, +{ + http: T, + conn_builder: B, +} + +impl HttpsConnector +where + B: connection::Builder, + ::Output: Unpin, +{ + /// Creates a new `Builder` used to create an `HttpsConnector`. + /// + /// This builder is created using the default hyper `HttpConnector`. To use a custom HTTP + /// connector, use `HttpsConnector::builder_with_http()`. + pub fn builder(conn_builder: B) -> Builder { + let mut http = HttpConnector::new(); + http.enforce_http(false); + + Builder::new(Self { http, conn_builder }) + } +} + +impl HttpsConnector +where + B: connection::Builder, + ::Output: Unpin, +{ + /// Creates a new `Builder` used to create an `HttpsConnector`. + pub fn builder_with_http(http: T, conn_builder: B) -> Builder { + Builder::new(Self { http, conn_builder }) + } +} + +impl Service for HttpsConnector +where + T: Service, + T::Response: Read + Write + Connection + Unpin + Send + 'static, + T::Future: Send + 'static, + T::Error: Into, + B: connection::Builder + Send + Sync + 'static, + ::Output: Unpin + Send, +{ + type Response = MaybeHttpsStream; + type Error = BoxError; + type Future = + Pin, BoxError>> + Send>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + match self.http.poll_ready(cx) { + Poll::Ready(Ok(())) => Poll::Ready(Ok(())), + Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), + Poll::Pending => Poll::Pending, + } + } + + fn call(&mut self, req: Uri) -> Self::Future { + if req.scheme() == Some(&http::uri::Scheme::HTTP) { + return Box::pin(async move { Err(UnsupportedScheme.into()) }); + } + + let builder = self.conn_builder.clone(); + let host = req.host().unwrap_or("").to_owned(); + let call = self.http.call(req); + Box::pin(async move { + let tcp = call.await.map_err(Into::into)?; + let tcp = TokioIo::new(tcp); + + let connector = TlsConnector::new(builder); + let tls = connector.connect(&host, tcp).await?; + + Ok(MaybeHttpsStream::Https(TokioIo::new(tls))) + }) + } +} + +pub struct Builder +where + B: connection::Builder, + ::Output: Unpin, +{ + connector: HttpsConnector, +} + +impl Builder +where + B: connection::Builder, + ::Output: Unpin, +{ + pub fn new(connector: HttpsConnector) -> Self { + Self { connector } + } + + pub fn build(self) -> HttpsConnector { + self.connector + } +} + +#[derive(Debug)] +struct UnsupportedScheme; + +impl fmt::Display for UnsupportedScheme { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("The provided URI scheme is not supported") + } +} + +impl std::error::Error for UnsupportedScheme {} + +#[cfg(test)] +mod tests { + use super::*; + use bytes::Bytes; + use http::status; + use http_body_util::{BodyExt, Empty}; + use hyper_util::{client::legacy::Client, rt::TokioExecutor}; + use std::{error::Error, str::FromStr}; + + #[tokio::test] + async fn test_get_request() -> Result<(), BoxError> { + let connector = HttpsConnector::builder(Config::default()).build(); + let client: Client<_, Empty> = + Client::builder(TokioExecutor::new()).build(connector); + + let uri = Uri::from_str("https://www.amazon.com")?; + let response = client.get(uri).await?; + assert_eq!(response.status(), status::StatusCode::OK); + + let body = response.into_body().collect().await?.to_bytes(); + assert!(!body.is_empty()); + + Ok(()) + } + + #[tokio::test] + async fn test_unsecure_http() -> Result<(), BoxError> { + let connector = HttpsConnector::builder(Config::default()).build(); + let client: Client<_, Empty> = + Client::builder(TokioExecutor::new()).build(connector); + + let uri = Uri::from_str("http://www.amazon.com")?; + let error = client.get(uri).await.unwrap_err(); + + // Ensure that an UnsupportedScheme error is returned when HTTP over TCP is attempted. + let _ = error + .source() + .unwrap() + .downcast_ref::() + .unwrap(); + + Ok(()) + } +} diff --git a/bindings/rust/s2n-tls-hyper/src/lib.rs b/bindings/rust/s2n-tls-hyper/src/lib.rs new file mode 100644 index 00000000000..26be62015e0 --- /dev/null +++ b/bindings/rust/s2n-tls-hyper/src/lib.rs @@ -0,0 +1,5 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +pub mod connector; +mod stream; diff --git a/bindings/rust/s2n-tls-hyper/src/stream.rs b/bindings/rust/s2n-tls-hyper/src/stream.rs new file mode 100644 index 00000000000..f144ce7a870 --- /dev/null +++ b/bindings/rust/s2n-tls-hyper/src/stream.rs @@ -0,0 +1,83 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use hyper::rt::{Read, ReadBufCursor, Write}; +use hyper_util::{ + client::legacy::connect::{Connected, Connection}, + rt::TokioIo, +}; +use s2n_tls::connection::Builder; +use s2n_tls_tokio::TlsStream; +use std::{ + io::Error, + pin::Pin, + task::{Context, Poll}, +}; + +pub enum MaybeHttpsStream +where + T: Read + Write + Connection + Unpin, + B: Builder, + ::Output: Unpin, +{ + Https(TokioIo, B::Output>>), +} + +impl Connection for MaybeHttpsStream +where + T: Read + Write + Connection + Unpin, + B: Builder, + ::Output: Unpin, +{ + fn connected(&self) -> Connected { + match self { + MaybeHttpsStream::Https(stream) => stream.inner().get_ref().connected(), + } + } +} + +impl Read for MaybeHttpsStream +where + T: Read + Write + Connection + Unpin, + B: Builder, + ::Output: Unpin, +{ + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: ReadBufCursor<'_>, + ) -> Poll> { + match Pin::get_mut(self) { + Self::Https(stream) => Pin::new(stream).poll_read(cx, buf), + } + } +} + +impl Write for MaybeHttpsStream +where + T: Read + Write + Connection + Unpin, + B: Builder, + ::Output: Unpin, +{ + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + match Pin::get_mut(self) { + Self::Https(stream) => Pin::new(stream).poll_write(cx, buf), + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match Pin::get_mut(self) { + MaybeHttpsStream::Https(stream) => Pin::new(stream).poll_flush(cx), + } + } + + fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + match Pin::get_mut(self) { + MaybeHttpsStream::Https(stream) => Pin::new(stream).poll_shutdown(cx), + } + } +} From 41b9ffe01439d019792957634a99401cc62a3279 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:15:17 -0400 Subject: [PATCH 2/9] add documentation --- bindings/rust/s2n-tls-hyper/src/connector.rs | 85 ++++++++++++++++++-- bindings/rust/s2n-tls-hyper/src/lib.rs | 42 ++++++++++ bindings/rust/s2n-tls-hyper/src/stream.rs | 17 ++++ 3 files changed, 137 insertions(+), 7 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index e1a69d6dfb4..b2ea54013fe 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -21,6 +21,27 @@ use tower_service::Service; type BoxError = Box; +/// hyper clients use a connector to send and receive HTTP requests over an underlying IO stream. By +/// default, hyper provides `hyper_util::client::legacy::connect::HttpConnector` for this purpose, +/// 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. The `HttpsConnector` can be provided to the +/// `hyper_util::client::legacy::Client` builder as follows: +/// ``` +/// use hyper_util::{ +/// client::legacy::Client, +/// rt::TokioExecutor, +/// }; +/// use s2n_tls_hyper::connector::HttpsConnector; +/// use s2n_tls::config::Config; +/// use bytes::Bytes; +/// use http_body_util::Empty; +/// +/// let connector = HttpsConnector::builder(Config::default()).build(); +/// let client: Client<_, Empty> = +/// Client::builder(TokioExecutor::new()).build(connector); +/// ``` #[derive(Clone)] pub struct HttpsConnector where @@ -38,13 +59,19 @@ where { /// Creates a new `Builder` used to create an `HttpsConnector`. /// - /// This builder is created using the default hyper `HttpConnector`. To use a custom HTTP + /// `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 builder is created using the default hyper `HttpConnector`. To use an existing HTTP /// connector, use `HttpsConnector::builder_with_http()`. pub fn builder(conn_builder: B) -> 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); - Builder::new(Self { http, conn_builder }) + Builder::new(http, conn_builder) } } @@ -54,11 +81,36 @@ where ::Output: Unpin, { /// Creates a new `Builder` used to create 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`. + /// + /// This API allows a `Builder` to be constructed with an existing HTTP connector, as follows: + /// ``` + /// use s2n_tls_hyper::connector::HttpsConnector; + /// use s2n_tls::config::Config; + /// use hyper_util::client::legacy::connect::HttpConnector; + /// + /// let mut http = HttpConnector::new(); + /// + /// // Ensure that the HTTP connector permits the HTTPS scheme. + /// http.enforce_http(false); + /// + /// let builder = HttpsConnector::builder_with_http(http, Config::default()); + /// ``` + /// + /// `HttpsConnector::builder()` can be used to create a new HTTP connector automatically. pub fn builder_with_http(http: T, conn_builder: B) -> Builder { - Builder::new(Self { http, conn_builder }) + Builder::new(http, conn_builder) } } +// hyper connectors MUST implement `hyper_util::client::legacy::connect::Connect`, which is an alias +// for the `tower_service::Service` trait where `Service` is implemented for `http::uri::Uri`, and +// `Service::Response` implements traits for compatibility with hyper: +// 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 where T: Service, @@ -82,6 +134,8 @@ 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(UnsupportedScheme.into()) }); } @@ -90,6 +144,9 @@ where let host = req.host().unwrap_or("").to_owned(); let call = self.http.call(req); Box::pin(async move { + // `HttpsConnector` wraps an HTTP connector that also implements `Service`. + // `call()` is invoked on the wrapped connector to get the underlying hyper TCP stream, + // which is converted into a tokio-compatible stream with `hyper_util::rt::TokioIo`. let tcp = call.await.map_err(Into::into)?; let tcp = TokioIo::new(tcp); @@ -101,12 +158,21 @@ where } } +/// The `Builder` struct configures and produces a new `HttpsConnector`. A Builder can be retrieved +/// with `HttpsConnector::builder()`, as follows: +/// ``` +/// use s2n_tls_hyper::connector::HttpsConnector; +/// use s2n_tls::config::Config; +/// +/// let builder = HttpsConnector::builder(Config::default()); +/// ``` pub struct Builder where B: connection::Builder, ::Output: Unpin, { - connector: HttpsConnector, + http: T, + conn_builder: B, } impl Builder @@ -114,12 +180,17 @@ where B: connection::Builder, ::Output: Unpin, { - pub fn new(connector: HttpsConnector) -> Self { - Self { connector } + /// Creates a new `Builder` used to create an `HttpsConnector`. + pub fn new(http: T, conn_builder: B) -> Self { + Self { http, conn_builder } } + /// Creates a new `HttpsConnector` from the specified builder configuration. pub fn build(self) -> HttpsConnector { - self.connector + HttpsConnector { + http: self.http, + conn_builder: self.conn_builder, + } } } diff --git a/bindings/rust/s2n-tls-hyper/src/lib.rs b/bindings/rust/s2n-tls-hyper/src/lib.rs index 26be62015e0..2223c005d70 100644 --- a/bindings/rust/s2n-tls-hyper/src/lib.rs +++ b/bindings/rust/s2n-tls-hyper/src/lib.rs @@ -1,5 +1,47 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +#![warn(missing_docs)] + +//! This crate provides compatibility structs for the [hyper](https://hyper.rs/) HTTP library, +//! allowing s2n-tls to be used as the underlying TLS implementation to negotiate HTTPS with hyper +//! clients. +//! +//! `s2n-tls-hyper` provides an `HttpsConnector` struct which is compatible with the +//! `hyper_util::client::legacy::Client` builder, allowing hyper clients to be constructed with +//! configurable s2n-tls connections. The following example demonstrates how to construct a hyper +//! client with s2n-tls: +//! +//! ``` +//! use std::str::FromStr; +//! use hyper_util::{ +//! client::legacy::Client, +//! rt::TokioExecutor, +//! }; +//! use s2n_tls_hyper::connector::HttpsConnector; +//! use s2n_tls::config::Config; +//! use bytes::Bytes; +//! use http_body_util::Empty; +//! use http::uri::Uri; +//! +//! async fn get_request() { +//! // An `HttpsConnector` is built with a `s2n_tls::connection::Builder`, such as an +//! // `s2n_tls::config::Config`, which allows for the underlying TLS connection to be configured. +//! let config = Config::default(); +//! +//! // The `HttpsConnector` wraps hyper's `HttpConnector`. `HttpsConnector::builder()` will create +//! // a new `HttpConnector` to wrap. +//! let connector = HttpsConnector::builder(Config::default()).build(); +//! +//! // The `HttpsConnector` can then be provided to the hyper Client builder, which can be used to +//! // send HTTP requests over HTTPS by specifying the HTTPS scheme in the URL. +//! let client: Client<_, Empty> = +//! Client::builder(TokioExecutor::new()).build(connector); +//! let uri = Uri::from_str("https://www.amazon.com").unwrap(); +//! let response = client.get(uri).await.unwrap(); +//! } +//! ``` + +/// Provides the `HttpsConnector` struct. pub mod connector; mod stream; diff --git a/bindings/rust/s2n-tls-hyper/src/stream.rs b/bindings/rust/s2n-tls-hyper/src/stream.rs index f144ce7a870..b6526c5f72b 100644 --- a/bindings/rust/s2n-tls-hyper/src/stream.rs +++ b/bindings/rust/s2n-tls-hyper/src/stream.rs @@ -14,12 +14,29 @@ use std::{ task::{Context, Poll}, }; +/// `MaybeHttpsStream` is a wrapper over a hyper TCP stream, T, allowing for TLS to be negotiated +/// over the TCP stream. +/// +/// While not currently implemented, the `MaybeHttpsStream` enum will provide an `Http` type +/// corresponding to the plain TCP stream, allowing for HTTP to be negotiated in addition to HTTPS +/// when the HTTP scheme is used. +/// +/// This struct is used to implement `tower_service::Service` for `HttpsConnector`, and shouldn't +/// need to be used directly. pub enum MaybeHttpsStream where T: Read + Write + Connection + Unpin, B: Builder, ::Output: Unpin, { + // T is the underlying hyper TCP stream, which is wrapped in a `TokioIo` type in order to make + // it compatible with tokio (implementing AsyncRead and AsyncWrite). This allows the TCP stream + // to be provided to the `s2n_tls_tokio::TlsStream`. + // + // `MaybeHttpsStream` MUST implement hyper's `Read` and `Write` traits. So, the `TlsStream` is + // wrapped in an additional `TokioIo` type, which already implements the conversion from hyper's + // traits to tokio's. This allows the `Read` and `Write` implementations for `MaybeHttpsStream` + // to simply call the `TokioIo` `poll` functions. Https(TokioIo, B::Output>>), } From 9761fcb4e8130e0352e8851098a9782fd83d97eb Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Fri, 21 Jun 2024 09:38:37 -0400 Subject: [PATCH 3/9] pr feedback --- bindings/rust/s2n-tls-hyper/src/connector.rs | 19 +------------- bindings/rust/s2n-tls-hyper/src/lib.rs | 2 +- bindings/rust/s2n-tls-hyper/src/stream.rs | 6 ++--- .../rust/s2n-tls-hyper/tests/web_client.rs | 25 +++++++++++++++++++ 4 files changed, 30 insertions(+), 22 deletions(-) create mode 100644 bindings/rust/s2n-tls-hyper/tests/web_client.rs diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index b2ea54013fe..13e7a3c2fb1 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -209,27 +209,10 @@ impl std::error::Error for UnsupportedScheme {} mod tests { use super::*; use bytes::Bytes; - use http::status; - use http_body_util::{BodyExt, Empty}; + use http_body_util::Empty; use hyper_util::{client::legacy::Client, rt::TokioExecutor}; use std::{error::Error, str::FromStr}; - #[tokio::test] - async fn test_get_request() -> Result<(), BoxError> { - let connector = HttpsConnector::builder(Config::default()).build(); - let client: Client<_, Empty> = - Client::builder(TokioExecutor::new()).build(connector); - - let uri = Uri::from_str("https://www.amazon.com")?; - let response = client.get(uri).await?; - assert_eq!(response.status(), status::StatusCode::OK); - - let body = response.into_body().collect().await?.to_bytes(); - assert!(!body.is_empty()); - - Ok(()) - } - #[tokio::test] async fn test_unsecure_http() -> Result<(), BoxError> { let connector = HttpsConnector::builder(Config::default()).build(); diff --git a/bindings/rust/s2n-tls-hyper/src/lib.rs b/bindings/rust/s2n-tls-hyper/src/lib.rs index 2223c005d70..b1de2a3616b 100644 --- a/bindings/rust/s2n-tls-hyper/src/lib.rs +++ b/bindings/rust/s2n-tls-hyper/src/lib.rs @@ -25,7 +25,7 @@ //! use http::uri::Uri; //! //! async fn get_request() { -//! // An `HttpsConnector` is built with a `s2n_tls::connection::Builder`, such as an +//! // An `HttpsConnector` is built with an `s2n_tls::connection::Builder`, such as an //! // `s2n_tls::config::Config`, which allows for the underlying TLS connection to be configured. //! let config = Config::default(); //! diff --git a/bindings/rust/s2n-tls-hyper/src/stream.rs b/bindings/rust/s2n-tls-hyper/src/stream.rs index b6526c5f72b..6a4897c5a0c 100644 --- a/bindings/rust/s2n-tls-hyper/src/stream.rs +++ b/bindings/rust/s2n-tls-hyper/src/stream.rs @@ -25,7 +25,7 @@ use std::{ /// need to be used directly. pub enum MaybeHttpsStream where - T: Read + Write + Connection + Unpin, + T: Read + Write + Unpin, B: Builder, ::Output: Unpin, { @@ -55,7 +55,7 @@ where impl Read for MaybeHttpsStream where - T: Read + Write + Connection + Unpin, + T: Read + Write + Unpin, B: Builder, ::Output: Unpin, { @@ -72,7 +72,7 @@ where impl Write for MaybeHttpsStream where - T: Read + Write + Connection + Unpin, + T: Read + Write + Unpin, B: Builder, ::Output: Unpin, { diff --git a/bindings/rust/s2n-tls-hyper/tests/web_client.rs b/bindings/rust/s2n-tls-hyper/tests/web_client.rs new file mode 100644 index 00000000000..2eda50834ed --- /dev/null +++ b/bindings/rust/s2n-tls-hyper/tests/web_client.rs @@ -0,0 +1,25 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +use bytes::Bytes; +use http::{status, Uri}; +use http_body_util::{BodyExt, Empty}; +use hyper_util::{client::legacy::Client, rt::TokioExecutor}; +use s2n_tls::config::Config; +use s2n_tls_hyper::connector::HttpsConnector; +use std::{error::Error, str::FromStr}; + +#[tokio::test] +async fn test_get_request() -> Result<(), Box> { + let connector = HttpsConnector::builder(Config::default()).build(); + let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(connector); + + let uri = Uri::from_str("https://www.amazon.com")?; + let response = client.get(uri).await?; + assert_eq!(response.status(), status::StatusCode::OK); + + let body = response.into_body().collect().await?.to_bytes(); + assert!(!body.is_empty()); + + Ok(()) +} From 5d4e4871fe5aa7ca798286bd735ae81f02165914 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Sun, 23 Jun 2024 11:20:21 -0400 Subject: [PATCH 4/9] better errors --- bindings/rust/s2n-tls-hyper/src/connector.rs | 37 ++++++++++---------- bindings/rust/s2n-tls-hyper/src/error.rs | 25 +++++++++++++ bindings/rust/s2n-tls-hyper/src/lib.rs | 4 +++ 3 files changed, 48 insertions(+), 18 deletions(-) create mode 100644 bindings/rust/s2n-tls-hyper/src/error.rs diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index 13e7a3c2fb1..40f6a82e4e2 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -use crate::stream::MaybeHttpsStream; +use crate::{error::Error, stream::MaybeHttpsStream}; use http::uri::Uri; use hyper::rt::{Read, Write}; use hyper_util::{ @@ -19,8 +19,6 @@ use std::{ }; use tower_service::Service; -type BoxError = Box; - /// hyper clients use a connector to send and receive HTTP requests over an underlying IO stream. By /// default, hyper provides `hyper_util::client::legacy::connect::HttpConnector` for this purpose, /// which sends and receives requests over TCP. @@ -116,19 +114,19 @@ where T: Service, T::Response: Read + Write + Connection + Unpin + Send + 'static, T::Future: Send + 'static, - T::Error: Into, + T::Error: Into>, B: connection::Builder + Send + Sync + 'static, ::Output: Unpin + Send, { type Response = MaybeHttpsStream; - type Error = BoxError; + type Error = Error; type Future = - Pin, BoxError>> + Send>>; + Pin, Error>> + Send>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { match self.http.poll_ready(cx) { Poll::Ready(Ok(())) => Poll::Ready(Ok(())), - Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())), + Poll::Ready(Err(e)) => Poll::Ready(Err(Error::HttpError(e.into()))), Poll::Pending => Poll::Pending, } } @@ -137,7 +135,7 @@ where // 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(UnsupportedScheme.into()) }); + return Box::pin(async move { Err(Error::InvalidScheme) }); } let builder = self.conn_builder.clone(); @@ -147,11 +145,14 @@ where // `HttpsConnector` wraps an HTTP connector that also implements `Service`. // `call()` is invoked on the wrapped connector to get the underlying hyper TCP stream, // which is converted into a tokio-compatible stream with `hyper_util::rt::TokioIo`. - let tcp = call.await.map_err(Into::into)?; + let tcp = call.await.map_err(|e| Error::HttpError(e.into()))?; let tcp = TokioIo::new(tcp); let connector = TlsConnector::new(builder); - let tls = connector.connect(&host, tcp).await?; + let tls = connector + .connect(&host, tcp) + .await + .map_err(|e| Error::TlsError(e.into()))?; Ok(MaybeHttpsStream::Https(TokioIo::new(tls))) }) @@ -211,10 +212,10 @@ mod tests { use bytes::Bytes; use http_body_util::Empty; use hyper_util::{client::legacy::Client, rt::TokioExecutor}; - use std::{error::Error, str::FromStr}; + use std::{error::Error as StdError, str::FromStr}; #[tokio::test] - async fn test_unsecure_http() -> Result<(), BoxError> { + async fn test_unsecure_http() -> Result<(), Box> { let connector = HttpsConnector::builder(Config::default()).build(); let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(connector); @@ -222,12 +223,12 @@ mod tests { let uri = Uri::from_str("http://www.amazon.com")?; let error = client.get(uri).await.unwrap_err(); - // Ensure that an UnsupportedScheme error is returned when HTTP over TCP is attempted. - let _ = error - .source() - .unwrap() - .downcast_ref::() - .unwrap(); + // Ensure that an InvalidScheme error is returned when HTTP over TCP is attempted. + let error = error.source().unwrap().downcast_ref::().unwrap(); + assert!(matches!(error, Error::InvalidScheme)); + + // Ensure that the error can produce a valid message + assert!(!error.to_string().is_empty()); Ok(()) } diff --git a/bindings/rust/s2n-tls-hyper/src/error.rs b/bindings/rust/s2n-tls-hyper/src/error.rs new file mode 100644 index 00000000000..8aa405cded7 --- /dev/null +++ b/bindings/rust/s2n-tls-hyper/src/error.rs @@ -0,0 +1,25 @@ +use std::fmt::{Display, Formatter}; + +/// Indicates which error occurred. +#[derive(Debug)] +#[non_exhaustive] +pub enum Error { + /// Indicates that the scheme in the URI provided to the `HttpsConnector` is invalid. + InvalidScheme, + /// Indicates that an error occurred in the underlying `HttpConnector`. + HttpError(Box), + /// Indicates that an error occurred in s2n-tls. + TlsError(s2n_tls::error::Error), +} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Error::InvalidScheme => write!(f, "The provided URI contains an invalid scheme."), + Error::HttpError(err) => write!(f, "{}", err), + Error::TlsError(err) => write!(f, "{}", err), + } + } +} + +impl std::error::Error for Error {} diff --git a/bindings/rust/s2n-tls-hyper/src/lib.rs b/bindings/rust/s2n-tls-hyper/src/lib.rs index b1de2a3616b..e79a462e1d3 100644 --- a/bindings/rust/s2n-tls-hyper/src/lib.rs +++ b/bindings/rust/s2n-tls-hyper/src/lib.rs @@ -44,4 +44,8 @@ /// Provides the `HttpsConnector` struct. pub mod connector; + +/// +pub mod error; + mod stream; From b3717e71b8c9129eb9be5bd1cc3bb0efcd180ea8 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon, 24 Jun 2024 08:47:30 -0400 Subject: [PATCH 5/9] pr feedback --- bindings/rust/s2n-tls-hyper/README.md | 2 +- bindings/rust/s2n-tls-hyper/src/connector.rs | 98 ++++--------------- bindings/rust/s2n-tls-hyper/src/lib.rs | 26 +++-- .../rust/s2n-tls-hyper/tests/web_client.rs | 2 +- 4 files changed, 30 insertions(+), 98 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/README.md b/bindings/rust/s2n-tls-hyper/README.md index 5e612865135..0f2e2d51c89 100644 --- a/bindings/rust/s2n-tls-hyper/README.md +++ b/bindings/rust/s2n-tls-hyper/README.md @@ -1,3 +1,3 @@ `s2n-tls-hyper` provides compatibility structs for [hyper](https://hyper.rs/), allowing s2n-tls to be used as the underlying TLS implementation with hyper clients. -This crate is currently under development and is extremely unstable. +This crate is currently being developed and is unstable. diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index 40f6a82e4e2..2f990c4b593 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -11,35 +11,18 @@ use hyper_util::{ use s2n_tls::{config::Config, connection}; use s2n_tls_tokio::TlsConnector; use std::{ - fmt, - fmt::{Debug, Formatter}, future::Future, pin::Pin, task::{Context, Poll}, }; use tower_service::Service; +/// hyper-compatible connector used to negotiate HTTPS. +/// /// hyper clients use a connector to send and receive HTTP requests over an underlying IO stream. By /// default, hyper provides `hyper_util::client::legacy::connect::HttpConnector` for this purpose, -/// 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. The `HttpsConnector` can be provided to the -/// `hyper_util::client::legacy::Client` builder as follows: -/// ``` -/// use hyper_util::{ -/// client::legacy::Client, -/// rt::TokioExecutor, -/// }; -/// use s2n_tls_hyper::connector::HttpsConnector; -/// use s2n_tls::config::Config; -/// use bytes::Bytes; -/// use http_body_util::Empty; -/// -/// let connector = HttpsConnector::builder(Config::default()).build(); -/// let client: Client<_, Empty> = -/// Client::builder(TokioExecutor::new()).build(connector); -/// ``` +/// 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 where @@ -55,21 +38,21 @@ where B: connection::Builder, ::Output: Unpin, { - /// Creates a new `Builder` used to create an `HttpsConnector`. + /// Creates 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 builder is created using the default hyper `HttpConnector`. To use an existing HTTP - /// connector, use `HttpsConnector::builder_with_http()`. - pub fn builder(conn_builder: B) -> Builder { + /// This API creates an `HttpsConnector` using the default hyper `HttpConnector`. To use an + /// existing HTTP connector, use `HttpsConnector::new_with_http()`. + pub fn new(conn_builder: B) -> HttpsConnector { 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); - Builder::new(http, conn_builder) + Self { http, conn_builder } } } @@ -78,12 +61,12 @@ where B: connection::Builder, ::Output: Unpin, { - /// Creates a new `Builder` used to create an `HttpsConnector`. + /// Creates 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 a `Builder` 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; @@ -94,12 +77,12 @@ where /// // Ensure that the HTTP connector permits the HTTPS scheme. /// http.enforce_http(false); /// - /// let builder = HttpsConnector::builder_with_http(http, Config::default()); + /// let connector = HttpsConnector::new_with_http(http, Config::default()); /// ``` /// - /// `HttpsConnector::builder()` can be used to create a new HTTP connector automatically. - pub fn builder_with_http(http: T, conn_builder: B) -> Builder { - Builder::new(http, conn_builder) + /// `HttpsConnector::new()` can be used to create the HTTP connector automatically. + pub fn new_with_http(http: T, conn_builder: B) -> HttpsConnector { + Self { http, conn_builder } } } @@ -152,60 +135,13 @@ where let tls = connector .connect(&host, tcp) .await - .map_err(|e| Error::TlsError(e.into()))?; + .map_err(|e| Error::TlsError(e))?; Ok(MaybeHttpsStream::Https(TokioIo::new(tls))) }) } } -/// The `Builder` struct configures and produces a new `HttpsConnector`. A Builder can be retrieved -/// with `HttpsConnector::builder()`, as follows: -/// ``` -/// use s2n_tls_hyper::connector::HttpsConnector; -/// use s2n_tls::config::Config; -/// -/// let builder = HttpsConnector::builder(Config::default()); -/// ``` -pub struct Builder -where - B: connection::Builder, - ::Output: Unpin, -{ - http: T, - conn_builder: B, -} - -impl Builder -where - B: connection::Builder, - ::Output: Unpin, -{ - /// Creates a new `Builder` used to create an `HttpsConnector`. - pub fn new(http: T, conn_builder: B) -> Self { - Self { http, conn_builder } - } - - /// Creates a new `HttpsConnector` from the specified builder configuration. - pub fn build(self) -> HttpsConnector { - HttpsConnector { - http: self.http, - conn_builder: self.conn_builder, - } - } -} - -#[derive(Debug)] -struct UnsupportedScheme; - -impl fmt::Display for UnsupportedScheme { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.write_str("The provided URI scheme is not supported") - } -} - -impl std::error::Error for UnsupportedScheme {} - #[cfg(test)] mod tests { use super::*; @@ -216,7 +152,7 @@ mod tests { #[tokio::test] async fn test_unsecure_http() -> Result<(), Box> { - let connector = HttpsConnector::builder(Config::default()).build(); + let connector = HttpsConnector::new(Config::default()); let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(connector); diff --git a/bindings/rust/s2n-tls-hyper/src/lib.rs b/bindings/rust/s2n-tls-hyper/src/lib.rs index e79a462e1d3..54fb2c016e4 100644 --- a/bindings/rust/s2n-tls-hyper/src/lib.rs +++ b/bindings/rust/s2n-tls-hyper/src/lib.rs @@ -24,28 +24,24 @@ //! use http_body_util::Empty; //! use http::uri::Uri; //! -//! async fn get_request() { -//! // An `HttpsConnector` is built with an `s2n_tls::connection::Builder`, such as an -//! // `s2n_tls::config::Config`, which allows for the underlying TLS connection to be configured. -//! let config = Config::default(); +//! // An `HttpsConnector` is built with an `s2n_tls::connection::Builder`, such as an +//! // `s2n_tls::config::Config`, which allows for the underlying TLS connection to be configured. +//! let config = Config::default(); //! -//! // The `HttpsConnector` wraps hyper's `HttpConnector`. `HttpsConnector::builder()` will create -//! // a new `HttpConnector` to wrap. -//! let connector = HttpsConnector::builder(Config::default()).build(); +//! // The `HttpsConnector` wraps hyper's `HttpConnector`. `HttpsConnector::new()` will create +//! // a new `HttpConnector` to wrap. +//! let connector = HttpsConnector::new(Config::default()); //! -//! // The `HttpsConnector` can then be provided to the hyper Client builder, which can be used to -//! // send HTTP requests over HTTPS by specifying the HTTPS scheme in the URL. -//! let client: Client<_, Empty> = -//! Client::builder(TokioExecutor::new()).build(connector); -//! let uri = Uri::from_str("https://www.amazon.com").unwrap(); -//! let response = client.get(uri).await.unwrap(); -//! } +//! // The `HttpsConnector` can then be provided to the hyper Client builder, which can be used to +//! // send HTTP requests over HTTPS by specifying the HTTPS scheme in the URL. +//! let client: Client<_, Empty> = +//! Client::builder(TokioExecutor::new()).build(connector); //! ``` /// Provides the `HttpsConnector` struct. pub mod connector; -/// +/// Provides errors returned by s2n-tls-hyper. pub mod error; mod stream; diff --git a/bindings/rust/s2n-tls-hyper/tests/web_client.rs b/bindings/rust/s2n-tls-hyper/tests/web_client.rs index 2eda50834ed..41a72970ed3 100644 --- a/bindings/rust/s2n-tls-hyper/tests/web_client.rs +++ b/bindings/rust/s2n-tls-hyper/tests/web_client.rs @@ -11,7 +11,7 @@ use std::{error::Error, str::FromStr}; #[tokio::test] async fn test_get_request() -> Result<(), Box> { - let connector = HttpsConnector::builder(Config::default()).build(); + let connector = HttpsConnector::new(Config::default()); let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(connector); let uri = Uri::from_str("https://www.amazon.com")?; From 989fd85ca00122f7a2a080024400ea1b5b02e97a Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:27:32 -0400 Subject: [PATCH 6/9] fix aws-lc link error --- .github/workflows/ci_rust.yml | 22 ++++++++++++++++---- bindings/rust/s2n-tls-hyper/src/connector.rs | 2 +- bindings/rust/s2n-tls-hyper/src/error.rs | 3 +++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_rust.yml b/.github/workflows/ci_rust.yml index 39191005f66..2246626253f 100644 --- a/.github/workflows/ci_rust.yml +++ b/.github/workflows/ci_rust.yml @@ -32,7 +32,7 @@ jobs: rustup override set stable # https://github.com/aws/aws-lc-rs/blob/main/aws-lc-fips-sys/README.md#build-prerequisites - # go required to build aws-lc-rs in FIPS mode + # go required for generate.sh to build aws-lc-rs in FIPS mode - name: Install go uses: actions/setup-go@v4 with: @@ -45,7 +45,8 @@ jobs: - name: Tests working-directory: ${{env.ROOT_PATH}} - run: cargo test --all-features + # Test all features except for FIPS, which is tested separately. + run: cargo test --features unstable-fingerprint,unstable-ktls,quic,pq,testing # Ensure that all tests pass with the default feature set - name: Default Tests @@ -158,7 +159,11 @@ jobs: run: cargo test --all-features fips: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macOS-latest] steps: - uses: actions/checkout@v3 with: @@ -185,7 +190,16 @@ jobs: - name: Test fips working-directory: ${{env.ROOT_PATH}} run: | - cargo test --features fips + # The doc tests fail to link to AWS-LC in FIPS mode due to + # https://github.com/rust-lang/cargo/issues/8531. The --tests flag is provided to disable + # the doc tests. The doc tests are tested in the generate test, where FIPS is disabled. + cargo test --tests --features fips + + # Test all features, including FIPS + - name: Test all + working-directory: ${{env.ROOT_PATH}} + run: | + cargo test --tests --all-features rustfmt: runs-on: ubuntu-latest diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index 2f990c4b593..7677cd955c7 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -135,7 +135,7 @@ where let tls = connector .connect(&host, tcp) .await - .map_err(|e| Error::TlsError(e))?; + .map_err(Error::TlsError)?; Ok(MaybeHttpsStream::Https(TokioIo::new(tls))) }) diff --git a/bindings/rust/s2n-tls-hyper/src/error.rs b/bindings/rust/s2n-tls-hyper/src/error.rs index 8aa405cded7..c6925a020dc 100644 --- a/bindings/rust/s2n-tls-hyper/src/error.rs +++ b/bindings/rust/s2n-tls-hyper/src/error.rs @@ -1,3 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + use std::fmt::{Display, Formatter}; /// Indicates which error occurred. From c2c10f7b9c7cb411f74f33ca95a73b9542296676 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Tue, 25 Jun 2024 18:27:28 -0400 Subject: [PATCH 7/9] pr feedback --- bindings/rust/s2n-tls-hyper/src/connector.rs | 47 +++++++++---------- bindings/rust/s2n-tls-hyper/src/stream.rs | 48 ++++++++++---------- 2 files changed, 46 insertions(+), 49 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/src/connector.rs b/bindings/rust/s2n-tls-hyper/src/connector.rs index 7677cd955c7..a160e605471 100644 --- a/bindings/rust/s2n-tls-hyper/src/connector.rs +++ b/bindings/rust/s2n-tls-hyper/src/connector.rs @@ -24,19 +24,15 @@ 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 -where - B: connection::Builder, - ::Output: Unpin, -{ - http: T, - conn_builder: B, +pub struct HttpsConnector { + http: Http, + conn_builder: Builder, } -impl HttpsConnector +impl HttpsConnector where - B: connection::Builder, - ::Output: Unpin, + Builder: connection::Builder, + ::Output: Unpin, { /// Creates a new `HttpsConnector`. /// @@ -45,7 +41,7 @@ where /// /// This API creates an `HttpsConnector` using the default hyper `HttpConnector`. To use an /// existing HTTP connector, use `HttpsConnector::new_with_http()`. - pub fn new(conn_builder: B) -> HttpsConnector { + pub fn new(conn_builder: Builder) -> HttpsConnector { let mut http = HttpConnector::new(); // By default, the `HttpConnector` only allows the HTTP URI scheme to be used. To negotiate @@ -56,10 +52,10 @@ where } } -impl HttpsConnector +impl HttpsConnector where - B: connection::Builder, - ::Output: Unpin, + Builder: connection::Builder, + ::Output: Unpin, { /// Creates a new `HttpsConnector`. /// @@ -81,7 +77,7 @@ where /// ``` /// /// `HttpsConnector::new()` can be used to create the HTTP connector automatically. - pub fn new_with_http(http: T, conn_builder: B) -> HttpsConnector { + pub fn new_with_http(http: Http, conn_builder: Builder) -> HttpsConnector { Self { http, conn_builder } } } @@ -92,19 +88,20 @@ 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 - T: Service, - T::Response: Read + Write + Connection + Unpin + Send + 'static, - T::Future: Send + 'static, - T::Error: Into>, - B: connection::Builder + Send + Sync + 'static, - ::Output: Unpin + Send, + 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, { - type Response = MaybeHttpsStream; + type Response = MaybeHttpsStream; type Error = Error; - type Future = - Pin, Error>> + Send>>; + type Future = Pin< + Box, Error>> + Send>, + >; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { match self.http.poll_ready(cx) { diff --git a/bindings/rust/s2n-tls-hyper/src/stream.rs b/bindings/rust/s2n-tls-hyper/src/stream.rs index 6a4897c5a0c..61bc59fe682 100644 --- a/bindings/rust/s2n-tls-hyper/src/stream.rs +++ b/bindings/rust/s2n-tls-hyper/src/stream.rs @@ -3,10 +3,10 @@ use hyper::rt::{Read, ReadBufCursor, Write}; use hyper_util::{ - client::legacy::connect::{Connected, Connection}, + client::legacy::connect::{Connected, Connection as HyperConnection}, rt::TokioIo, }; -use s2n_tls::connection::Builder; +use s2n_tls::connection; use s2n_tls_tokio::TlsStream; use std::{ io::Error, @@ -14,8 +14,8 @@ use std::{ task::{Context, Poll}, }; -/// `MaybeHttpsStream` is a wrapper over a hyper TCP stream, T, allowing for TLS to be negotiated -/// over the TCP stream. +/// `MaybeHttpsStream` is a wrapper over a hyper TCP stream, `Transport`, allowing for TLS to be +/// negotiated over the TCP stream. /// /// While not currently implemented, the `MaybeHttpsStream` enum will provide an `Http` type /// corresponding to the plain TCP stream, allowing for HTTP to be negotiated in addition to HTTPS @@ -23,28 +23,28 @@ use std::{ /// /// This struct is used to implement `tower_service::Service` for `HttpsConnector`, and shouldn't /// need to be used directly. -pub enum MaybeHttpsStream +pub enum MaybeHttpsStream where - T: Read + Write + Unpin, - B: Builder, - ::Output: Unpin, + Transport: Read + Write + Unpin, + Builder: connection::Builder, + ::Output: Unpin, { - // T is the underlying hyper TCP stream, which is wrapped in a `TokioIo` type in order to make - // it compatible with tokio (implementing AsyncRead and AsyncWrite). This allows the TCP stream - // to be provided to the `s2n_tls_tokio::TlsStream`. + // `Transport` is the underlying hyper TCP stream, which is wrapped in a `TokioIo` type in order + // to make it compatible with tokio (implementing AsyncRead and AsyncWrite). This allows the TCP + // stream to be provided to the `s2n_tls_tokio::TlsStream`. // // `MaybeHttpsStream` MUST implement hyper's `Read` and `Write` traits. So, the `TlsStream` is // wrapped in an additional `TokioIo` type, which already implements the conversion from hyper's // traits to tokio's. This allows the `Read` and `Write` implementations for `MaybeHttpsStream` // to simply call the `TokioIo` `poll` functions. - Https(TokioIo, B::Output>>), + Https(TokioIo, Builder::Output>>), } -impl Connection for MaybeHttpsStream +impl HyperConnection for MaybeHttpsStream where - T: Read + Write + Connection + Unpin, - B: Builder, - ::Output: Unpin, + Transport: Read + Write + HyperConnection + Unpin, + Builder: connection::Builder, + ::Output: Unpin, { fn connected(&self) -> Connected { match self { @@ -53,11 +53,11 @@ where } } -impl Read for MaybeHttpsStream +impl Read for MaybeHttpsStream where - T: Read + Write + Unpin, - B: Builder, - ::Output: Unpin, + Transport: Read + Write + Unpin, + Builder: connection::Builder, + ::Output: Unpin, { fn poll_read( self: Pin<&mut Self>, @@ -70,11 +70,11 @@ where } } -impl Write for MaybeHttpsStream +impl Write for MaybeHttpsStream where - T: Read + Write + Unpin, - B: Builder, - ::Output: Unpin, + Transport: Read + Write + Unpin, + Builder: connection::Builder, + ::Output: Unpin, { fn poll_write( self: Pin<&mut Self>, From 7c9250f483ca4c207465e4a6d8a5423d67c54763 Mon Sep 17 00:00:00 2001 From: Sam Clark <3758302+goatgoose@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:56:21 -0400 Subject: [PATCH 8/9] update package versions Co-authored-by: Lindsay Stewart --- bindings/rust/s2n-tls-hyper/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/rust/s2n-tls-hyper/Cargo.toml b/bindings/rust/s2n-tls-hyper/Cargo.toml index a8321bc31a7..f29f27e0c93 100644 --- a/bindings/rust/s2n-tls-hyper/Cargo.toml +++ b/bindings/rust/s2n-tls-hyper/Cargo.toml @@ -12,8 +12,8 @@ license = "Apache-2.0" default = [] [dependencies] -s2n-tls = { version = "=0.2.7", path = "../s2n-tls" } -s2n-tls-tokio = { version = "=0.2.7", path = "../s2n-tls-tokio" } +s2n-tls = { version = "=0.2.9", path = "../s2n-tls" } +s2n-tls-tokio = { version = "=0.2.9", path = "../s2n-tls-tokio" } hyper = { version = "1" } hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1"] } tower-service = { version = "0.3" } From 09f2fb651a7ba401aed58e4db1d7d299ecf85bce Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Wed, 31 Jul 2024 23:29:08 -0700 Subject: [PATCH 9/9] Remove deleted feature --- .github/workflows/ci_rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_rust.yml b/.github/workflows/ci_rust.yml index bafb820b91d..48e93cd8aa8 100644 --- a/.github/workflows/ci_rust.yml +++ b/.github/workflows/ci_rust.yml @@ -47,7 +47,7 @@ jobs: - name: Tests working-directory: ${{env.ROOT_PATH}} # Test all features except for FIPS, which is tested separately. - run: cargo test --features unstable-fingerprint,unstable-ktls,quic,pq,testing + run: cargo test --features unstable-fingerprint,unstable-ktls,quic,pq # Ensure that all tests pass with the default feature set - name: Default Tests