From b4bf6013946f7e09cd816867f3d2c4a267f33b33 Mon Sep 17 00:00:00 2001 From: Alessandro Ghedini Date: Fri, 26 Sep 2025 07:32:00 +0100 Subject: [PATCH] Remove support for Hyper v0 --- .github/workflows/ci.yml | 2 - Cargo.toml | 2 - hyper-boring/Cargo.toml | 18 +- hyper-boring/src/lib.rs | 7 +- hyper-boring/src/v0.rs | 345 --------------------------------------- hyper-boring/src/v1.rs | 3 - hyper-boring/tests/v0.rs | 156 ------------------ hyper-boring/tests/v1.rs | 4 +- 8 files changed, 7 insertions(+), 530 deletions(-) delete mode 100644 hyper-boring/src/v0.rs delete mode 100644 hyper-boring/tests/v0.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8f17085..110d8ba7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -371,5 +371,3 @@ jobs: name: Run `rpk,underscore-wildcards` tests - run: cargo test --features pq-experimental,rpk,underscore-wildcards name: Run `pq-experimental,rpk,underscore-wildcards` tests - - run: cargo test -p hyper-boring --features hyper1 - name: Run hyper 1.0 tests for hyper-boring diff --git a/Cargo.toml b/Cargo.toml index 451b2d99..8fd3835e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,10 +40,8 @@ anyhow = "1" antidote = "1.0.0" http = "1" http-body-util = "0.1.2" -http_old = { package = "http", version = "0.2" } hyper = "1" hyper-util = "0.1.6" -hyper_old = { package = "hyper", version = "0.14", default-features = false } linked_hash_set = "0.1" openssl-macros = "0.1.1" tower = "0.4" diff --git a/hyper-boring/Cargo.toml b/hyper-boring/Cargo.toml index 91f74424..77a8a788 100644 --- a/hyper-boring/Cargo.toml +++ b/hyper-boring/Cargo.toml @@ -16,10 +16,6 @@ features = ["pq-experimental"] rustdoc-args = ["--cfg", "docsrs"] [features] -default = ["runtime"] - -runtime = ["hyper_old/runtime"] - # Use a FIPS-validated version of boringssl. fips = ["tokio-boring/fips"] @@ -39,29 +35,23 @@ fips-link-precompiled = ["tokio-boring/fips-link-precompiled"] # Enables experimental post-quantum crypto (https://blog.cloudflare.com/post-quantum-for-all/) pq-experimental = ["tokio-boring/pq-experimental"] -# Enable Hyper 1 support -hyper1 = ["dep:http", "dep:hyper", "dep:hyper-util", "dep:tower-service"] - [dependencies] antidote = { workspace = true } -http = { workspace = true, optional = true } -http_old = { workspace = true } -hyper = { workspace = true, optional = true } -hyper-util = { workspace = true, optional = true, features = ["client", "client-legacy"] } -hyper_old = { workspace = true, features = ["client"] } +http = { workspace = true } +hyper = { workspace = true } +hyper-util = { workspace = true, features = ["client", "client-legacy"] } linked_hash_set = { workspace = true } boring = { workspace = true } tokio = { workspace = true } tokio-boring = { workspace = true } tower-layer = { workspace = true } -tower-service = { workspace = true, optional = true } +tower-service = { workspace = true } [dev-dependencies] bytes = { workspace = true } http-body-util = { workspace = true } hyper-util = { workspace = true, features = ["http1", "http2", "service", "tokio"] } hyper = { workspace = true, features = ["server"] } -hyper_old = { workspace = true, features = [ "full" ] } tokio = { workspace = true, features = [ "full" ] } tower = { workspace = true, features = ["util"] } futures = { workspace = true } diff --git a/hyper-boring/src/lib.rs b/hyper-boring/src/lib.rs index 1822e135..0e1f2b17 100644 --- a/hyper-boring/src/lib.rs +++ b/hyper-boring/src/lib.rs @@ -11,12 +11,9 @@ use std::sync::LazyLock; use tokio_boring::SslStream; mod cache; -mod v0; -/// Hyper 1 support. -#[cfg(feature = "hyper1")] -pub mod v1; +mod v1; -pub use self::v0::*; +pub use self::v1::*; fn key_index() -> Result, ErrorStack> { static IDX: LazyLock> = LazyLock::new(|| Ssl::new_ex_index().unwrap()); diff --git a/hyper-boring/src/v0.rs b/hyper-boring/src/v0.rs deleted file mode 100644 index 03368d32..00000000 --- a/hyper-boring/src/v0.rs +++ /dev/null @@ -1,345 +0,0 @@ -use crate::cache::{SessionCache, SessionKey}; -use crate::{key_index, HttpsLayerSettings, MaybeHttpsStream}; -use antidote::Mutex; -use boring::error::ErrorStack; -use boring::ssl::{ - ConnectConfiguration, Ssl, SslConnector, SslConnectorBuilder, SslMethod, SslRef, - SslSessionCacheMode, -}; -use http_old::uri::Scheme; -use hyper_old::client::connect::{Connected, Connection}; -use hyper_old::client::HttpConnector; -use hyper_old::service::Service; -use hyper_old::Uri; -use std::error::Error; -use std::future::Future; -use std::net; -use std::pin::Pin; -use std::sync::Arc; -use std::task::{Context, Poll}; -use std::{fmt, io}; -use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; -use tower_layer::Layer; - -/// A Connector using BoringSSL to support `http` and `https` schemes. -#[derive(Clone)] -pub struct HttpsConnector { - http: T, - inner: Inner, -} - -#[cfg(feature = "runtime")] -impl HttpsConnector { - /// Creates a a new `HttpsConnector` using default settings. - /// - /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both - /// HTTP/2 and HTTP/1.1. - /// - /// Requires the `runtime` Cargo feature. - pub fn new() -> Result, ErrorStack> { - let mut http = HttpConnector::new(); - http.enforce_http(false); - - HttpsLayer::new().map(|l| l.layer(http)) - } -} - -impl HttpsConnector -where - S: Service + Send, - S::Error: Into>, - S::Future: Unpin + Send + 'static, - T: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, -{ - /// Creates a new `HttpsConnector`. - /// - /// The session cache configuration of `ssl` will be overwritten. - pub fn with_connector( - http: S, - ssl: SslConnectorBuilder, - ) -> Result, ErrorStack> { - HttpsLayer::with_connector(ssl).map(|l| l.layer(http)) - } - - /// Registers a callback which can customize the configuration of each connection. - /// - /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), - /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] - /// instead. - pub fn set_callback(&mut self, callback: F) - where - F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.callback = Some(Arc::new(callback)); - } - - /// Registers a callback which can customize the `Ssl` of each connection. - pub fn set_ssl_callback(&mut self, callback: F) - where - F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.ssl_callback = Some(Arc::new(callback)); - } -} - -/// A layer which wraps services in an `HttpsConnector`. -pub struct HttpsLayer { - inner: Inner, -} - -#[derive(Clone)] -struct Inner { - ssl: SslConnector, - cache: Arc>, - callback: Option, - ssl_callback: Option, -} - -type Callback = - Arc Result<(), ErrorStack> + Sync + Send>; -type SslCallback = Arc Result<(), ErrorStack> + Sync + Send>; - -impl HttpsLayer { - /// Creates a new `HttpsLayer` with default settings. - /// - /// ALPN is configured to support both HTTP/1 and HTTP/1.1. - pub fn new() -> Result { - let mut ssl = SslConnector::builder(SslMethod::tls())?; - - ssl.set_alpn_protos(b"\x02h2\x08http/1.1")?; - - Self::with_connector(ssl) - } - - /// Creates a new `HttpsLayer`. - /// - /// The session cache configuration of `ssl` will be overwritten. - pub fn with_connector(ssl: SslConnectorBuilder) -> Result { - Self::with_connector_and_settings(ssl, Default::default()) - } - - /// Creates a new `HttpsLayer` with settings - pub fn with_connector_and_settings( - mut ssl: SslConnectorBuilder, - settings: HttpsLayerSettings, - ) -> Result { - let cache = Arc::new(Mutex::new(SessionCache::with_capacity( - settings.session_cache_capacity, - ))); - - ssl.set_session_cache_mode(SslSessionCacheMode::CLIENT); - - ssl.set_new_session_callback({ - let cache = cache.clone(); - move |ssl, session| { - if let Some(key) = key_index().ok().and_then(|idx| ssl.ex_data(idx)) { - cache.lock().insert(key.clone(), session); - } - } - }); - - Ok(HttpsLayer { - inner: Inner { - ssl: ssl.build(), - cache, - callback: None, - ssl_callback: None, - }, - }) - } - - /// Registers a callback which can customize the configuration of each connection. - /// - /// Unsuitable to change verify hostflags (with `config.param_mut().set_hostflags(…)`), - /// as they are reset after the callback is executed. Use [`Self::set_ssl_callback`] - /// instead. - pub fn set_callback(&mut self, callback: F) - where - F: Fn(&mut ConnectConfiguration, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.callback = Some(Arc::new(callback)); - } - - /// Registers a callback which can customize the `Ssl` of each connection. - pub fn set_ssl_callback(&mut self, callback: F) - where - F: Fn(&mut SslRef, &Uri) -> Result<(), ErrorStack> + 'static + Sync + Send, - { - self.inner.ssl_callback = Some(Arc::new(callback)); - } -} - -impl Layer for HttpsLayer { - type Service = HttpsConnector; - - fn layer(&self, inner: S) -> HttpsConnector { - HttpsConnector { - http: inner, - inner: self.inner.clone(), - } - } -} - -impl Inner { - fn setup_ssl(&self, uri: &Uri, host: &str) -> Result { - let mut conf = self.ssl.configure()?; - - if let Some(ref callback) = self.callback { - callback(&mut conf, uri)?; - } - - let key = SessionKey { - host: host.to_string(), - port: uri.port_u16().unwrap_or(443), - }; - - if let Some(session) = self.cache.lock().get(&key) { - unsafe { - conf.set_session(&session)?; - } - } - - let idx = key_index()?; - conf.set_ex_data(idx, key); - - let mut ssl = conf.into_ssl(host)?; - - if let Some(ref ssl_callback) = self.ssl_callback { - ssl_callback(&mut ssl, uri)?; - } - - Ok(ssl) - } -} - -impl Service for HttpsConnector -where - S: Service + Send, - S::Error: Into>, - S::Future: Unpin + Send + 'static, - S::Response: AsyncRead + AsyncWrite + Connection + Unpin + fmt::Debug + Sync + Send + 'static, -{ - type Response = MaybeHttpsStream; - type Error = Box; - #[allow(clippy::type_complexity)] - type Future = Pin> + Send>>; - - fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { - self.http.poll_ready(cx).map_err(Into::into) - } - - fn call(&mut self, uri: Uri) -> Self::Future { - let is_tls_scheme = uri - .scheme() - .map(|s| s == &Scheme::HTTPS || s.as_str() == "wss") - .unwrap_or(false); - - let tls_setup = if is_tls_scheme { - Some((self.inner.clone(), uri.clone())) - } else { - None - }; - - let connect = self.http.call(uri); - - let f = async { - let conn = connect.await.map_err(Into::into)?; - - let (inner, uri) = match tls_setup { - Some((inner, uri)) => (inner, uri), - None => return Ok(MaybeHttpsStream::Http(conn)), - }; - - let mut host = uri.host().ok_or("URI missing host")?; - - // If `host` is an IPv6 address, we must strip away the square brackets that surround - // it (otherwise, boring will fail to parse the host as an IP address, eventually - // causing the handshake to fail due a hostname verification error). - if !host.is_empty() { - let last = host.len() - 1; - let mut chars = host.chars(); - - if (chars.next(), chars.last()) == (Some('['), Some(']')) - && host[1..last].parse::().is_ok() - { - host = &host[1..last]; - } - } - - let ssl = inner.setup_ssl(&uri, host)?; - let stream = tokio_boring::SslStreamBuilder::new(ssl, conn) - .connect() - .await?; - - Ok(MaybeHttpsStream::Https(stream)) - }; - - Box::pin(f) - } -} - -impl Connection for MaybeHttpsStream -where - T: Connection, -{ - fn connected(&self) -> Connected { - match self { - MaybeHttpsStream::Http(s) => s.connected(), - MaybeHttpsStream::Https(s) => { - let mut connected = s.get_ref().connected(); - - if s.ssl().selected_alpn_protocol() == Some(b"h2") { - connected = connected.negotiated_h2(); - } - - connected - } - } - } -} - -impl AsyncRead for MaybeHttpsStream -where - T: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_read( - mut self: Pin<&mut Self>, - ctx: &mut Context<'_>, - buf: &mut ReadBuf<'_>, - ) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_read(ctx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_read(ctx, buf), - } - } -} - -impl AsyncWrite for MaybeHttpsStream -where - T: AsyncRead + AsyncWrite + Unpin, -{ - fn poll_write( - mut self: Pin<&mut Self>, - ctx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_write(ctx, buf), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_write(ctx, buf), - } - } - - fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_flush(ctx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_flush(ctx), - } - } - - fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll> { - match &mut *self { - MaybeHttpsStream::Http(s) => Pin::new(s).poll_shutdown(ctx), - MaybeHttpsStream::Https(s) => Pin::new(s).poll_shutdown(ctx), - } - } -} diff --git a/hyper-boring/src/v1.rs b/hyper-boring/src/v1.rs index e1f9a43d..f4cb0168 100644 --- a/hyper-boring/src/v1.rs +++ b/hyper-boring/src/v1.rs @@ -29,14 +29,11 @@ pub struct HttpsConnector { inner: Inner, } -#[cfg(feature = "runtime")] impl HttpsConnector { /// Creates a a new `HttpsConnector` using default settings. /// /// The Hyper `HttpConnector` is used to perform the TCP socket connection. ALPN is configured to support both /// HTTP/2 and HTTP/1.1. - /// - /// Requires the `runtime` Cargo feature. pub fn new() -> Result, ErrorStack> { let mut http = HttpConnector::new(); http.enforce_http(false); diff --git a/hyper-boring/tests/v0.rs b/hyper-boring/tests/v0.rs deleted file mode 100644 index f52e1851..00000000 --- a/hyper-boring/tests/v0.rs +++ /dev/null @@ -1,156 +0,0 @@ -use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; -use futures::StreamExt; -use hyper_boring::HttpsConnector; -use hyper_old::client::HttpConnector; -use hyper_old::server::conn::Http; -use hyper_old::{service, Response}; -use hyper_old::{Body, Client}; -use std::convert::Infallible; -use std::{io, iter}; -use tokio::net::TcpListener; - -#[tokio::test] -#[cfg(feature = "runtime")] -async fn google() { - let ssl = HttpsConnector::new().unwrap(); - let client = Client::builder() - .pool_max_idle_per_host(0) - .build::<_, Body>(ssl); - - for _ in 0..3 { - let resp = client - .get("https://www.google.com".parse().unwrap()) - .await - .expect("connection should succeed"); - let mut body = resp.into_body(); - while body.next().await.transpose().unwrap().is_some() {} - } -} - -#[tokio::test] -async fn localhost() { - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - let port = addr.port(); - - let server = async move { - let mut acceptor = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); - acceptor.set_session_id_context(b"test").unwrap(); - acceptor - .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) - .unwrap(); - acceptor - .set_certificate_chain_file("tests/test/cert.pem") - .unwrap(); - let acceptor = acceptor.build(); - - for _ in 0..3 { - let stream = listener.accept().await.unwrap().0; - let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); - - let service = - service::service_fn(|_| async { Ok::<_, io::Error>(Response::new(Body::empty())) }); - - Http::new() - .http1_keep_alive(false) - .serve_connection(stream, service) - .await - .unwrap(); - } - }; - tokio::spawn(server); - - let resolver = - tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); - - let mut connector = HttpConnector::new_with_resolver(resolver); - - connector.enforce_http(false); - - let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); - - ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); - - use std::fs::File; - use std::io::Write; - - let file = File::create("../target/keyfile.log").unwrap(); - ssl.set_keylog_callback(move |_, line| { - let _ = writeln!(&file, "{line}"); - }); - - let ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); - let client = Client::builder().build::<_, Body>(ssl); - - for _ in 0..3 { - let resp = client - .get(format!("https://foobar.com:{port}").parse().unwrap()) - .await - .unwrap(); - assert!(resp.status().is_success(), "{}", resp.status()); - let mut body = resp.into_body(); - while body.next().await.transpose().unwrap().is_some() {} - } -} - -#[tokio::test] -async fn alpn_h2() { - use boring::ssl::{self, AlpnError}; - - let listener = TcpListener::bind("127.0.0.1:0").await.unwrap(); - let addr = listener.local_addr().unwrap(); - let port = addr.port(); - - let server = async move { - let mut acceptor = SslAcceptor::mozilla_modern(SslMethod::tls()).unwrap(); - acceptor - .set_certificate_chain_file("tests/test/cert.pem") - .unwrap(); - acceptor - .set_private_key_file("tests/test/key.pem", SslFiletype::PEM) - .unwrap(); - acceptor.set_alpn_select_callback(|_, client| { - ssl::select_next_proto(b"\x02h2", client).ok_or(AlpnError::NOACK) - }); - let acceptor = acceptor.build(); - - let stream = listener.accept().await.unwrap().0; - let stream = tokio_boring::accept(&acceptor, stream).await.unwrap(); - assert_eq!(stream.ssl().selected_alpn_protocol().unwrap(), b"h2"); - - let service = - service::service_fn(|_| async { Ok::<_, io::Error>(Response::new(Body::empty())) }); - - Http::new() - .http2_only(true) - .serve_connection(stream, service) - .await - .unwrap(); - }; - tokio::spawn(server); - - let resolver = - tower::service_fn(move |_name| async move { Ok::<_, Infallible>(iter::once(addr)) }); - - let mut connector = HttpConnector::new_with_resolver(resolver); - - connector.enforce_http(false); - - let mut ssl = SslConnector::builder(SslMethod::tls()).unwrap(); - - ssl.set_ca_file("tests/test/root-ca.pem").unwrap(); - - let mut ssl = HttpsConnector::with_connector(connector, ssl).unwrap(); - - ssl.set_ssl_callback(|ssl, _| ssl.set_alpn_protos(b"\x02h2\x08http/1.1")); - - let client = Client::builder().build::<_, Body>(ssl); - - let resp = client - .get(format!("https://foobar.com:{port}").parse().unwrap()) - .await - .unwrap(); - assert!(resp.status().is_success(), "{}", resp.status()); - let mut body = resp.into_body(); - while body.next().await.transpose().unwrap().is_some() {} -} diff --git a/hyper-boring/tests/v1.rs b/hyper-boring/tests/v1.rs index 441caea6..4082d2ce 100644 --- a/hyper-boring/tests/v1.rs +++ b/hyper-boring/tests/v1.rs @@ -1,11 +1,9 @@ -#![cfg(feature = "hyper1")] - use boring::ssl::{SslAcceptor, SslConnector, SslFiletype, SslMethod}; use bytes::Bytes; use futures::StreamExt; use http_body_util::{BodyStream, Empty}; use hyper::{service, Response}; -use hyper_boring::v1::HttpsConnector; +use hyper_boring::HttpsConnector; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use hyper_util::rt::{TokioExecutor, TokioIo};