Skip to content

Commit

Permalink
feat(s2n-tls-hyper): Add support for negotiating HTTP/2
Browse files Browse the repository at this point in the history
  • Loading branch information
goatgoose committed Nov 21, 2024
1 parent 72ae26d commit e87a725
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 7 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci_rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ jobs:
working-directory: ${{env.ROOT_PATH}}
run: cargo test

- name: "Feature Tests: Fingerprint, kTLS, QUIC, and PQ"
- name: "Feature Tests: Fingerprint, kTLS, QUIC, PQ, and http2"
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
run: cargo test --features unstable-fingerprint,unstable-ktls,quic,pq,http2

- name: "Feature Test: Renegotiate"
working-directory: ${{env.ROOT_PATH}}
Expand Down
13 changes: 10 additions & 3 deletions bindings/rust/s2n-tls-hyper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,22 @@ license = "Apache-2.0"
publish = false

[features]
default = []
default = ["http1"]
http1 = ["hyper-util/http1"]
http2 = ["hyper-util/http2", "dep:hashbrown", "dep:tokio-util"]

[dependencies]
s2n-tls = { version = "=0.3.7", path = "../s2n-tls" }
s2n-tls-tokio = { version = "=0.3.7", path = "../s2n-tls-tokio" }
hyper = { version = "1" }
hyper-util = { version = "0.1", features = ["client-legacy", "tokio", "http1"] }
hyper-util = { version = "0.1", features = ["client-legacy", "tokio"] }
tower-service = { version = "0.3" }
http = { version= "1" }
http = { version = "1" }

# Newer versions require Rust 1.65, see https://github.com/aws/s2n-tls/issues/4242.
hashbrown = { version = "=0.15.0", optional = true }
# Newer versions require Rust 1.70, see https://github.com/aws/s2n-tls/issues/4395.
tokio-util = { version = "=0.7.11", optional = true }

[dev-dependencies]
tokio = { version = "1", features = ["macros", "test-util"] }
Expand Down
11 changes: 10 additions & 1 deletion bindings/rust/s2n-tls-hyper/src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,16 @@ where
{
fn connected(&self) -> Connected {
match self {
MaybeHttpsStream::Https(stream) => stream.inner().get_ref().connected(),
MaybeHttpsStream::Https(stream) => {
let connected = stream.inner().get_ref().connected();
let conn = stream.inner().as_ref();
match conn.application_protocol() {
// Inform hyper that HTTP/2 was negotiated in the ALPN.
#[cfg(feature = "http2")]
Some(b"h2") => connected.negotiated_h2(),
_ => connected,
}
}
}
}
}
Expand Down
50 changes: 49 additions & 1 deletion bindings/rust/s2n-tls-hyper/tests/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use crate::common::InsecureAcceptAllCertificatesHandler;
use bytes::Bytes;
use common::echo::serve_echo;
use http::{Method, Request, Uri};
use http::{Method, Request, Uri, Version};
use http_body_util::{BodyExt, Empty, Full};
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
use s2n_tls::{
Expand Down Expand Up @@ -215,3 +215,51 @@ async fn error_matching() -> Result<(), Box<dyn Error + Send + Sync>> {
server_task.abort();
Ok(())
}

#[tokio::test]
async fn http2() -> Result<(), Box<dyn Error + Send + Sync>> {
let server_config = {
let mut builder = common::config()?;
builder.set_application_protocol_preference(["h2"])?;
builder.build()?
};

for send_h2 in [true, false] {
let client_config = {
let mut builder = common::config()?;
if send_h2 {
builder.set_application_protocol_preference(["h2"])?;
}
builder.build()?
};

common::echo::make_echo_request(server_config.clone(), |port| async move {
let connector = HttpsConnector::new(client_config);
let client: Client<_, Empty<Bytes>> =
Client::builder(TokioExecutor::new()).build(connector);

let uri = Uri::from_str(format!("https://localhost:{}", port).as_str())?;
let response = client.get(uri).await?;
assert_eq!(response.status(), 200);

// Ensure that HTTP/2 is negotiated when included in the ALPN.
#[cfg(feature = "http2")]
let expected_version = match send_h2 {
true => Version::HTTP_2,
false => Version::HTTP_11,
};

// If the http2 feature isn't enabled, then HTTP/1 should be negotiated even if HTTP/2
// was included in the ALPN.
#[cfg(not(feature = "http2"))]
let expected_version = Version::HTTP_11;

assert_eq!(response.version(), expected_version);

Ok(())
})
.await?;
}

Ok(())
}

0 comments on commit e87a725

Please sign in to comment.