-
Notifications
You must be signed in to change notification settings - Fork 724
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(bindings): Add hyper compatibility crate #4617
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
977ca2b
feat(bindings): Add hyper compatibility crate
goatgoose 41b9ffe
add documentation
goatgoose 9761fcb
pr feedback
goatgoose 5d4e487
better errors
goatgoose b3717e7
pr feedback
goatgoose 989fd85
fix aws-lc link error
goatgoose c2c10f7
pr feedback
goatgoose 2f01f09
Merge branch 'main' into hyper-crate-init
goatgoose 69bc951
Merge branch 'main' into hyper-crate-init
dougch c44c109
Merge branch 'main' into hyper-crate-init
lrstewart 7c9250f
update package versions
goatgoose 303067e
Merge branch 'main' into hyper-crate-init
lrstewart 09f2fb6
Remove deleted feature
lrstewart File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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.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" } | ||
http = { version= "1" } | ||
|
||
[dev-dependencies] | ||
tokio = { version = "1", features = ["macros", "test-util"] } | ||
http-body-util = "0.1" | ||
bytes = "1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 being developed and is unstable. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
use crate::{error::Error, 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::{ | ||
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. | ||
#[derive(Clone)] | ||
pub struct HttpsConnector<Http, Builder = Config> { | ||
http: Http, | ||
conn_builder: Builder, | ||
} | ||
|
||
impl<Builder> HttpsConnector<HttpConnector, Builder> | ||
where | ||
Builder: connection::Builder, | ||
<Builder as connection::Builder>::Output: Unpin, | ||
{ | ||
/// 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 creates an `HttpsConnector` using the default hyper `HttpConnector`. To use an | ||
/// existing HTTP connector, use `HttpsConnector::new_with_http()`. | ||
pub fn new(conn_builder: Builder) -> HttpsConnector<HttpConnector, 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 } | ||
} | ||
} | ||
|
||
impl<Http, Builder> HttpsConnector<Http, Builder> | ||
where | ||
Builder: connection::Builder, | ||
<Builder as connection::Builder>::Output: Unpin, | ||
{ | ||
/// 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 an `HttpsConnector` 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); | ||
lrstewart marked this conversation as resolved.
Show resolved
Hide resolved
|
||
/// | ||
/// let connector = HttpsConnector::new_with_http(http, Config::default()); | ||
/// ``` | ||
/// | ||
/// `HttpsConnector::new()` can be used to create the HTTP connector automatically. | ||
pub fn new_with_http(http: Http, conn_builder: Builder) -> HttpsConnector<Http, Builder> { | ||
Self { 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<Http, Builder> Service<Uri> for HttpsConnector<Http, Builder> | ||
where | ||
Http: Service<Uri>, | ||
Http::Response: Read + Write + Connection + Unpin + Send + 'static, | ||
Http::Future: Send + 'static, | ||
Http::Error: Into<Box<dyn std::error::Error + Send + Sync>>, | ||
Builder: connection::Builder + Send + Sync + 'static, | ||
<Builder as connection::Builder>::Output: Unpin + Send, | ||
{ | ||
type Response = MaybeHttpsStream<Http::Response, Builder>; | ||
type Error = Error; | ||
type Future = Pin< | ||
Box<dyn Future<Output = Result<MaybeHttpsStream<Http::Response, Builder>, Error>> + Send>, | ||
>; | ||
|
||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | ||
match self.http.poll_ready(cx) { | ||
Poll::Ready(Ok(())) => Poll::Ready(Ok(())), | ||
Poll::Ready(Err(e)) => Poll::Ready(Err(Error::HttpError(e.into()))), | ||
Poll::Pending => Poll::Pending, | ||
} | ||
} | ||
|
||
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) }); | ||
} | ||
|
||
let builder = self.conn_builder.clone(); | ||
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<Uri>`. | ||
// `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(|e| Error::HttpError(e.into()))?; | ||
let tcp = TokioIo::new(tcp); | ||
|
||
let connector = TlsConnector::new(builder); | ||
let tls = connector | ||
.connect(&host, tcp) | ||
.await | ||
.map_err(Error::TlsError)?; | ||
|
||
Ok(MaybeHttpsStream::Https(TokioIo::new(tls))) | ||
}) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use bytes::Bytes; | ||
use http_body_util::Empty; | ||
use hyper_util::{client::legacy::Client, rt::TokioExecutor}; | ||
use std::{error::Error as StdError, str::FromStr}; | ||
|
||
#[tokio::test] | ||
async fn test_unsecure_http() -> Result<(), Box<dyn StdError>> { | ||
let connector = HttpsConnector::new(Config::default()); | ||
let client: Client<_, Empty<Bytes>> = | ||
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 InvalidScheme error is returned when HTTP over TCP is attempted. | ||
let error = error.source().unwrap().downcast_ref::<Error>().unwrap(); | ||
assert!(matches!(error, Error::InvalidScheme)); | ||
|
||
// Ensure that the error can produce a valid message | ||
assert!(!error.to_string().is_empty()); | ||
|
||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// 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. | ||
#[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<dyn std::error::Error + Send + Sync>), | ||
/// 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 {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
goatgoose marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#![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; | ||
//! | ||
//! // 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::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<Bytes>> = | ||
//! 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; |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was getting link errors when building the new doc tests with AWS-LC-FIPS:
https://github.com/aws/s2n-tls/actions/runs/9646640129/job/26603549106?pr=4617
It seems like this is related to this issue: rust-lang/cargo#8531. AWS-LC works around this issue by disabling doc tests when testing FIPS, so I did this as well.