diff --git a/Cargo.lock b/Cargo.lock index 95a9ade30a..8eb99ecb40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -147,6 +147,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-tungstenite", + "tokio-util", "tower", "tower-http", "tower-layer", diff --git a/axum/Cargo.toml b/axum/Cargo.toml index 901eb2f175..bad12a3b35 100644 --- a/axum/Cargo.toml +++ b/axum/Cargo.toml @@ -126,6 +126,7 @@ serde_urlencoded = { version = "0.7", optional = true } sha1 = { version = "0.10", optional = true } tokio = { package = "tokio", version = "1.44", features = ["time"], optional = true } tokio-tungstenite = { version = "0.28.0", optional = true } +tokio-util = "0.7.4" tracing = { version = "0.1", default-features = false, optional = true } # doc dependencies diff --git a/axum/src/serve/connection/hyper.rs b/axum/src/serve/connection/hyper.rs new file mode 100644 index 0000000000..f5dbc2ae0e --- /dev/null +++ b/axum/src/serve/connection/hyper.rs @@ -0,0 +1,153 @@ +use std::{ + convert::Infallible, + error::Error as StdError, + future::Future, + pin::Pin, + task::{Context, Poll}, +}; + +use axum_core::{body::Body, extract::Request, response::Response}; +use http_body::Body as HttpBody; +use hyper::{ + body::Incoming, + rt::{Read as HyperRead, Write as HyperWrite}, + service::HttpService as HyperHttpService, + service::Service as HyperService, +}; +#[cfg(feature = "http1")] +use hyper_util::rt::TokioTimer; +use hyper_util::{ + rt::{TokioExecutor, TokioIo}, + server::conn::auto::{Builder, HttpServerConnExec, UpgradeableConnection}, + service::TowerToHyperService, +}; +use pin_project_lite::pin_project; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio_util::sync::{CancellationToken, WaitForCancellationFutureOwned}; +use tower::{Service, ServiceExt}; + +use super::{Connection, ConnectionBuilder}; + +pin_project! { + /// An implementation of [`Connection`] when serving with hyper. + pub struct HyperConnection<'a, I, S: HyperHttpService, E> { + #[pin] + inner: UpgradeableConnection<'a, I, S, E>, + #[pin] + shutdown: Option, + } +} + +impl Connection for HyperConnection<'_, I, S, E> +where + S: HyperService, Response = Response> + Send, + S::Error: Into>, + S::Future: Send + 'static, + I: HyperRead + HyperWrite + Unpin + Send + 'static, + B: HttpBody + Send + 'static, + B::Data: Send, + B::Error: Into>, + E: HttpServerConnExec + Send + Sync, +{ + fn poll_connection( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + let mut this = self.project(); + if let Some(shutdown) = this.shutdown.as_mut().as_pin_mut() { + if shutdown.poll(cx).is_ready() { + trace!("signal received in connection, starting graceful shutdown"); + this.inner.as_mut().graceful_shutdown(); + this.shutdown.set(None); + } + } + this.inner.poll(cx) + } +} + +/// An implementation of [`ConnectionBuilder`] when serving with hyper. +#[derive(Debug, Clone)] +pub struct Hyper { + builder: Builder, + shutdown: CancellationToken, +} + +impl Hyper { + /// Create a new [`ConnectionBuilder`] implementation from a + /// [`hyper_util::server::conn::auto::Builder`]. This builder may be set up in any way that the + /// user may need. + /// + /// # Example + /// + /// ```rust + /// # async { + /// # use axum::Router; + /// # use axum::serve::{Hyper, serve}; + /// # use hyper_util::server::conn::auto::Builder; + /// # use hyper_util::rt::TokioExecutor; + /// let mut builder = Builder::new(TokioExecutor::new()).http2_only(); + /// builder.http2().enable_connect_protocol(); + /// let connection_builder = Hyper::new(builder); + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// serve(listener, Router::new()).with_connection_builder(connection_builder).await.unwrap(); + /// # }; + /// ``` + #[must_use] + pub fn new(builder: Builder) -> Self { + Self { + builder, + shutdown: CancellationToken::new(), + } + } +} + +impl Default for Hyper { + fn default() -> Self { + #[allow(unused_mut)] + let mut builder = Builder::new(TokioExecutor::new()); + + // Enable Hyper's default HTTP/1 request header timeout. + #[cfg(feature = "http1")] + builder.http1().timer(TokioTimer::new()); + + // CONNECT protocol needed for HTTP/2 websockets + #[cfg(feature = "http2")] + builder.http2().enable_connect_protocol(); + + Self::new(builder) + } +} + +impl ConnectionBuilder for Hyper +where + Io: AsyncRead + AsyncWrite + Send + Unpin + 'static, + S: Service, Error = Infallible> + Clone + Send + 'static, + S::Future: Send, + B: HttpBody + Send + 'static, + B::Data: Send, + B::Error: Into>, +{ + fn build_connection(&mut self, io: Io, service: S) -> impl Connection { + fn map_body(req: Request) -> Request { + req.map(Body::new) + } + + let hyper_service = TowerToHyperService::new( + service.map_request(map_body as fn(Request) -> Request), + ); + + let io = TokioIo::new(io); + let hyper_connection = self + .builder + .serve_connection_with_upgrades(io, hyper_service); + + HyperConnection { + inner: hyper_connection, + shutdown: Some(self.shutdown.clone().cancelled_owned()), + } + } + + fn graceful_shutdown(&mut self) { + self.shutdown.cancel(); + } +} diff --git a/axum/src/serve/connection/mod.rs b/axum/src/serve/connection/mod.rs new file mode 100644 index 0000000000..8f02b880e8 --- /dev/null +++ b/axum/src/serve/connection/mod.rs @@ -0,0 +1,52 @@ +use std::{ + error::Error as StdError, + future::Future, + ops::DerefMut, + pin::Pin, + task::{Context, Poll}, +}; + +pub use hyper::{Hyper, HyperConnection}; + +#[cfg(any(feature = "http1", feature = "http2"))] +mod hyper; + +/// Types that can handle connections accepted by a [`Listener`]. +/// +/// [`Listener`]: crate::serve::Listener +pub trait ConnectionBuilder: Clone { + /// Take an accepted connection from the [`Listener`] (for example a `TcpStream`) and handle + /// requests on it using the provided service (usually a [`Router`](crate::Router)). + /// + /// [`Listener`]: crate::serve::Listener + fn build_connection(&mut self, io: Io, service: S) -> impl Connection; + + /// Signal to all ongoing connections that the server is shutting down. + fn graceful_shutdown(&mut self); +} + +/// A connection returned by [`ConnectionBuilder`]. +/// +/// This type must be driven by calling [`Connection::poll_connection`]. +/// +/// Note that each [`Connection`] may handle multiple requests. +pub trait Connection: Send { + /// Poll the connection. + fn poll_connection( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>>; +} + +impl Connection for Pin +where + Ptr: DerefMut + Send, + Fut: Future>> + Send, +{ + fn poll_connection( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll>> { + self.as_mut().poll(cx) + } +} diff --git a/axum/src/serve/mod.rs b/axum/src/serve/mod.rs index 1f50c9ec83..1a97f05d51 100644 --- a/axum/src/serve/mod.rs +++ b/axum/src/serve/mod.rs @@ -4,26 +4,23 @@ use std::{ convert::Infallible, error::Error as StdError, fmt::Debug, - future::{Future, IntoFuture}, + future::{poll_fn, Future, IntoFuture}, io, marker::PhantomData, pin::pin, }; -use axum_core::{body::Body, extract::Request, response::Response}; -use futures_util::FutureExt; +use axum_core::{extract::Request, response::Response}; use http_body::Body as HttpBody; -use hyper::body::Incoming; -use hyper_util::rt::{TokioExecutor, TokioIo, TokioTimer}; -#[cfg(any(feature = "http1", feature = "http2"))] -use hyper_util::{server::conn::auto::Builder, service::TowerToHyperService}; use tokio::sync::watch; use tower::ServiceExt as _; use tower_service::Service; +mod connection; mod listener; -pub use self::listener::{ConnLimiter, ConnLimiterIo, Listener, ListenerExt, TapIo}; +pub use connection::{Connection, ConnectionBuilder, Hyper, HyperConnection}; +pub use listener::{ConnLimiter, ConnLimiterIo, Listener, ListenerExt, TapIo}; /// Serve the service with the supplied listener. /// @@ -97,8 +94,8 @@ pub use self::listener::{ConnLimiter, ConnLimiterIo, Listener, ListenerExt, TapI /// [`Handler`]: crate::handler::Handler /// [`HandlerWithoutStateExt::into_make_service_with_connect_info`]: crate::handler::HandlerWithoutStateExt::into_make_service_with_connect_info /// [`HandlerService::into_make_service_with_connect_info`]: crate::handler::HandlerService::into_make_service_with_connect_info -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -pub fn serve(listener: L, make_service: M) -> Serve +#[cfg(feature = "tokio")] +pub fn serve(listener: L, make_service: M) -> Serve where L: Listener, M: for<'a> Service, Error = Infallible, Response = S>, @@ -110,22 +107,24 @@ where { Serve { listener, + connection_builder: Hyper::default(), make_service, _marker: PhantomData, } } /// Future returned by [`serve`]. -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] +#[cfg(feature = "tokio")] #[must_use = "futures must be awaited or polled"] -pub struct Serve { +pub struct Serve { listener: L, + connection_builder: C, make_service: M, _marker: PhantomData S>, } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl Serve +#[cfg(feature = "tokio")] +impl Serve where L: Listener, { @@ -155,12 +154,13 @@ where /// /// Similarly to [`serve`], although this future resolves to `io::Result<()>`, it will never /// error. It returns `Ok(())` only after the `signal` future completes. - pub fn with_graceful_shutdown(self, signal: F) -> WithGracefulShutdown + pub fn with_graceful_shutdown(self, signal: F) -> WithGracefulShutdown where F: Future + Send + 'static, { WithGracefulShutdown { listener: self.listener, + connection_builder: self.connection_builder, make_service: self.make_service, signal, _marker: PhantomData, @@ -173,8 +173,8 @@ where } } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl Serve +#[cfg(feature = "tokio")] +impl Serve where L: Listener, L::Addr: Debug, @@ -186,49 +186,87 @@ where B::Data: Send, B::Error: Into>, { - async fn run(self) -> ! { + /// Serve with a custom [`ConnectionBuilder`] implementation. + /// + /// # Example + /// + /// ```rust + /// # async { + /// # use axum::Router; + /// # use axum::serve::{Hyper, serve}; + /// let connection_builder = Hyper::default(); + /// let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); + /// serve(listener, Router::new()).with_connection_builder(connection_builder).await.unwrap(); + /// # }; + /// ``` + pub fn with_connection_builder(self, connection_builder: C2) -> Serve + where + C2: ConnectionBuilder + Send + 'static, + { + Serve { + listener: self.listener, + connection_builder, + make_service: self.make_service, + _marker: PhantomData, + } + } + + async fn run(self) -> ! + where + C: ConnectionBuilder + Send + 'static, + { let Self { mut listener, + connection_builder, mut make_service, _marker, } = self; - let (signal_tx, _signal_rx) = watch::channel(()); let (_close_tx, close_rx) = watch::channel(()); loop { let (io, remote_addr) = listener.accept().await; - handle_connection(&mut make_service, &signal_tx, &close_rx, io, remote_addr).await; + handle_connection( + &mut make_service, + &close_rx, + io, + remote_addr, + connection_builder.clone(), + ) + .await; } } } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl Debug for Serve +#[cfg(feature = "tokio")] +impl Debug for Serve where - L: Debug + 'static, + L: Debug, + C: Debug, M: Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { listener, + connection_builder, make_service, _marker: _, } = self; - let mut s = f.debug_struct("Serve"); - s.field("listener", listener) - .field("make_service", make_service); - - s.finish() + f.debug_struct("Serve") + .field("listener", listener) + .field("connection_builder", connection_builder) + .field("make_service", make_service) + .finish() } } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl IntoFuture for Serve +#[cfg(feature = "tokio")] +impl IntoFuture for Serve where L: Listener, L::Addr: Debug, + C: ConnectionBuilder + Send + 'static, M: for<'a> Service, Error = Infallible, Response = S> + Send + 'static, for<'a> >>::Future: Send, S: Service, Error = Infallible> + Clone + Send + 'static, @@ -246,17 +284,18 @@ where } /// Serve future with graceful shutdown enabled. -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] +#[cfg(feature = "tokio")] #[must_use = "futures must be awaited or polled"] -pub struct WithGracefulShutdown { +pub struct WithGracefulShutdown { listener: L, + connection_builder: C, make_service: M, signal: F, _marker: PhantomData S>, } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl WithGracefulShutdown +#[cfg(feature = "tokio")] +impl WithGracefulShutdown where L: Listener, { @@ -266,11 +305,12 @@ where } } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl WithGracefulShutdown +#[cfg(feature = "tokio")] +impl WithGracefulShutdown where L: Listener, L::Addr: Debug, + C: ConnectionBuilder + Send + 'static, M: for<'a> Service, Error = Infallible, Response = S> + Send + 'static, for<'a> >>::Future: Send, S: Service, Error = Infallible> + Clone + Send + 'static, @@ -285,6 +325,7 @@ where mut listener, mut make_service, signal, + mut connection_builder, _marker, } = self; @@ -298,15 +339,25 @@ where let (close_tx, close_rx) = watch::channel(()); loop { - let (io, remote_addr) = tokio::select! { - conn = listener.accept() => conn, - _ = signal_tx.closed() => { + use futures_util::future::{select, Either}; + + match select(pin!(listener.accept()), pin!(signal_tx.closed())).await { + Either::Left(((io, remote_addr), _)) => { + handle_connection( + &mut make_service, + &close_rx, + io, + remote_addr, + connection_builder.clone(), + ) + .await; + } + Either::Right(((), _)) => { + connection_builder.graceful_shutdown(); trace!("signal received, not accepting new connections"); break; } - }; - - handle_connection(&mut make_service, &signal_tx, &close_rx, io, remote_addr).await; + } } drop(close_rx); @@ -320,10 +371,11 @@ where } } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl Debug for WithGracefulShutdown +#[cfg(feature = "tokio")] +impl Debug for WithGracefulShutdown where - L: Debug + 'static, + L: Debug, + C: Debug, M: Debug, S: Debug, F: Debug, @@ -331,6 +383,7 @@ where fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { listener, + connection_builder, make_service, signal, _marker: _, @@ -338,17 +391,19 @@ where f.debug_struct("WithGracefulShutdown") .field("listener", listener) + .field("connection_builder", connection_builder) .field("make_service", make_service) .field("signal", signal) .finish() } } -#[cfg(all(feature = "tokio", any(feature = "http1", feature = "http2")))] -impl IntoFuture for WithGracefulShutdown +#[cfg(feature = "tokio")] +impl IntoFuture for WithGracefulShutdown where L: Listener, L::Addr: Debug, + C: ConnectionBuilder + Send + 'static, M: for<'a> Service, Error = Infallible, Response = S> + Send + 'static, for<'a> >>::Future: Send, S: Service, Error = Infallible> + Clone + Send + 'static, @@ -369,15 +424,16 @@ where } } -async fn handle_connection( +async fn handle_connection( make_service: &mut M, - signal_tx: &watch::Sender<()>, close_rx: &watch::Receiver<()>, io: ::Io, remote_addr: ::Addr, + mut connection_builder: C, ) where L: Listener, L::Addr: Debug, + C: ConnectionBuilder + Send + 'static, M: for<'a> Service, Error = Infallible, Response = S> + Send + 'static, for<'a> >>::Future: Send, S: Service, Error = Infallible> + Clone + Send + 'static, @@ -386,8 +442,6 @@ async fn handle_connection( B::Data: Send, B::Error: Into>, { - let io = TokioIo::new(io); - trace!("connection {remote_addr:?} accepted"); make_service @@ -401,41 +455,20 @@ async fn handle_connection( remote_addr, }) .await - .unwrap_or_else(|err| match err {}) - .map_request(|req: Request| req.map(Body::new)); + .unwrap_or_else(|err| match err {}); - let hyper_service = TowerToHyperService::new(tower_service); - let signal_tx = signal_tx.clone(); let close_rx = close_rx.clone(); tokio::spawn(async move { - #[allow(unused_mut)] - let mut builder = Builder::new(TokioExecutor::new()); + let connection = connection_builder.build_connection(io, tower_service); - // Enable Hyper's default HTTP/1 request header timeout. - #[cfg(feature = "http1")] - builder.http1().timer(TokioTimer::new()); + let mut connection = pin!(connection); - // CONNECT protocol needed for HTTP/2 websockets - #[cfg(feature = "http2")] - builder.http2().enable_connect_protocol(); + let connection_future = poll_fn(|cx| connection.as_mut().poll_connection(cx)); - let mut conn = pin!(builder.serve_connection_with_upgrades(io, hyper_service)); - let mut signal_closed = pin!(signal_tx.closed().fuse()); - - loop { - tokio::select! { - result = conn.as_mut() => { - if let Err(_err) = result { - trace!("failed to serve connection: {_err:#}"); - } - break; - } - _ = &mut signal_closed => { - trace!("signal received in task, starting graceful shutdown"); - conn.as_mut().graceful_shutdown(); - } - } + #[allow(unused_variables)] // Without tracing, the binding is unused. + if let Err(err) = connection_future.await { + trace!(error = debug(err), "failed to serve connection"); } drop(close_rx); @@ -452,7 +485,7 @@ pub struct IncomingStream<'a, L> where L: Listener, { - io: &'a TokioIo, + io: &'a L::Io, remote_addr: L::Addr, } @@ -462,7 +495,7 @@ where { /// Get a reference to the inner IO type. pub fn io(&self) -> &L::Io { - self.io.inner() + self.io } /// Returns the remote address that this stream is bound to. @@ -525,7 +558,7 @@ mod tests { body::to_bytes, handler::{Handler, HandlerWithoutStateExt}, routing::get, - serve::ListenerExt, + serve::{Connection, ConnectionBuilder, ListenerExt}, Router, ServiceExt, }; @@ -842,4 +875,37 @@ mod tests { app.into_make_service(), ); } + + #[crate::test] + async fn serving_without_hyper() { + #[derive(Clone)] + struct OkGenerator; + + impl ConnectionBuilder for OkGenerator { + fn build_connection(&mut self, mut io: Io, _service: S) -> impl Connection { + Box::pin(async move { + io.write_all(b"OK").await?; + Ok(()) + }) + } + + fn graceful_shutdown(&mut self) {} + } + + let (mut client, server) = io::duplex(1024); + let listener = ReadyListener(Some(server)); + + let app = Router::new().route("/", get(|| async { "Hello, World!" })); + + tokio::spawn( + serve(listener, app) + .with_connection_builder(OkGenerator) + .into_future(), + ); + + let mut buf = [0u8; 2]; + client.read_exact(&mut buf).await.unwrap(); + + assert_eq!(&buf, b"OK"); + } } diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 58bb532dde..653c4456f4 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.0" @@ -129,7 +120,7 @@ dependencies = [ "memchr", "proc-macro2", "quote", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "serde_derive", "syn", @@ -147,6 +138,45 @@ dependencies = [ "winnow", ] +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom 7.1.3", + "num-traits", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -203,6 +233,28 @@ dependencies = [ "sha2 0.9.9", ] +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "async-trait" version = "0.1.88" @@ -240,16 +292,31 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-lc-fips-sys" +version = "0.13.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "57900537c00a0565a35b63c4c281b372edfc9744b072fd4a3b414350a8f5ed48" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "regex", +] [[package]] name = "aws-lc-rs" -version = "1.13.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" dependencies = [ + "aws-lc-fips-sys", "aws-lc-sys", "untrusted 0.7.1", "zeroize", @@ -257,9 +324,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.29.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" dependencies = [ "bindgen", "cc", @@ -300,6 +367,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-tungstenite", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -374,21 +442,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "backtrace" -version = "0.3.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" -dependencies = [ - "addr2line", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-targets 0.52.6", -] - [[package]] name = "base64" version = "0.13.1" @@ -450,25 +503,22 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags", "cexpr", "clang-sys", - "itertools", - "lazy_static", - "lazycell", + "itertools 0.12.1", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", "syn", - "which", ] [[package]] @@ -525,6 +575,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "cfg_aliases", +] + [[package]] name = "brotli" version = "8.0.1" @@ -589,10 +648,11 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.25" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -604,7 +664,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -709,6 +769,26 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const_format" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -839,6 +919,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "csv" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde_core", +] + +[[package]] +name = "csv-core" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" +dependencies = [ + "memchr", +] + [[package]] name = "darling" version = "0.20.11" @@ -966,6 +1067,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom 7.1.3", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.4.0" @@ -1163,6 +1278,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enum-as-inner" version = "0.6.1" @@ -1226,7 +1347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" dependencies = [ "futures-core", - "nom", + "nom 7.1.3", "pin-project-lite", ] @@ -1653,6 +1774,34 @@ dependencies = [ "tower", ] +[[package]] +name = "example-serve-with-hyper-rustls" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum", + "http-body", + "hyper", + "rustls", + "rustls-pemfile", + "tokio", + "tokio-rustls", + "tokio-util", + "tower", + "tracing", +] + +[[package]] +name = "example-serve-with-rama" +version = "0.1.0" +dependencies = [ + "axum", + "pin-project-lite", + "rama", + "tokio", + "tower", +] + [[package]] name = "example-simple-router-wasm" version = "0.1.0" @@ -1918,6 +2067,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + [[package]] name = "flate2" version = "1.1.1" @@ -1925,6 +2080,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", + "libz-sys", "miniz_oxide", ] @@ -1936,6 +2092,7 @@ checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" dependencies = [ "futures-core", "futures-sink", + "nanorand", "spin", ] @@ -1963,7 +2120,28 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1972,6 +2150,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1997,12 +2181,37 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fslock" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.31" @@ -2082,6 +2291,7 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -2093,6 +2303,21 @@ dependencies = [ "slab", ] +[[package]] +name = "generator" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "libc", + "log", + "rustversion", + "windows-link 0.1.1", + "windows-result", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2130,12 +2355,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gimli" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" - [[package]] name = "glob" version = "0.3.2" @@ -2586,12 +2805,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.16.1", ] [[package]] @@ -2631,6 +2850,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -2683,12 +2911,6 @@ dependencies = [ "spin", ] -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.172" @@ -2702,7 +2924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if 1.0.0", - "windows-targets 0.53.5", + "windows-targets 0.52.6", ] [[package]] @@ -2722,10 +2944,15 @@ dependencies = [ ] [[package]] -name = "linux-raw-sys" -version = "0.4.15" +name = "libz-sys" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] [[package]] name = "linux-raw-sys" @@ -2766,6 +2993,22 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if 1.0.0", + "generator", + "pin-utils", + "scoped-tls", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", +] + [[package]] name = "lru-slab" version = "0.1.2" @@ -2845,6 +3088,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "md5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0" + [[package]] name = "memchr" version = "2.7.4" @@ -3074,6 +3323,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.16", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -3092,7 +3350,16 @@ dependencies = [ ] [[package]] -name = "nom" +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" @@ -3101,6 +3368,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3205,12 +3481,12 @@ dependencies = [ ] [[package]] -name = "object" -version = "0.36.7" +name = "oid-registry" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ - "memchr", + "asn1-rs", ] [[package]] @@ -3237,7 +3513,7 @@ checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags", "cfg-if 1.0.0", - "foreign-types", + "foreign-types 0.3.2", "libc", "once_cell", "openssl-macros", @@ -3478,6 +3754,15 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -3509,6 +3794,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psl" +version = "2.1.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdecbdf79d08eb7f62ea92e07e9c639a39700bb9dc3755f3c66fb6aeb8d7ee12" +dependencies = [ + "psl-types", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + [[package]] name = "quanta" version = "0.12.5" @@ -3535,7 +3835,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "socket2 0.5.10", "thiserror 2.0.12", @@ -3555,7 +3855,7 @@ dependencies = [ "lru-slab", "rand 0.9.1", "ring", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "rustls-pki-types", "slab", @@ -3600,6 +3900,452 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "rama" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9abba26d2a4a60eb94a17224755035ce03f06fd1f3448915cbd8bf02dd838d" +dependencies = [ + "pin-project-lite", + "rama-core", + "rama-crypto", + "rama-dns", + "rama-http", + "rama-http-backend", + "rama-http-core", + "rama-net", + "rama-socks5", + "rama-tcp", + "rama-tls-boring", + "rama-tower", + "rama-ua", + "rama-unix", + "rama-utils", + "rama-ws", + "rustversion", + "tokio", +] + +[[package]] +name = "rama-boring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c52217ff947d630f3807cde9f8fe4cc69005b17a1e81fc99fa432cb3fb9c78" +dependencies = [ + "bitflags", + "foreign-types 0.5.0", + "libc", + "openssl-macros", + "rama-boring-sys", +] + +[[package]] +name = "rama-boring-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7fd7057828ca9fa148c704f1bf0fff12f6367891212c9c0caad63ae17902a7" +dependencies = [ + "autocfg", + "bindgen", + "cmake", + "fs_extra", + "fslock", +] + +[[package]] +name = "rama-boring-tokio" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07c71d66f9fa0b52b02055b94c779e98fc27d228e67105c6e528cfd1f19a7a4" +dependencies = [ + "rama-boring", + "rama-boring-sys", + "tokio", +] + +[[package]] +name = "rama-core" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea03771324b3d27a4d5d450bdd288fdeac87638f7cdb5870f3d1da6503542483" +dependencies = [ + "async-stream", + "bytes", + "futures", + "parking_lot", + "pin-project-lite", + "rama-error", + "rama-macros", + "rama-utils", + "tokio", + "tokio-graceful", + "tracing", +] + +[[package]] +name = "rama-crypto" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91965fc4cb4bc45bd2980f6388f97dbc955bcf164fdbd81fc0e9155c4e9abe2f" +dependencies = [ + "aws-lc-rs", + "base64 0.22.1", + "rama-core", + "rama-utils", + "rcgen", + "rustls-pki-types", + "serde", + "serde_json", + "x509-parser 0.18.0", +] + +[[package]] +name = "rama-dns" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7556de219395b9b170f9fdbff10ee6a1c022749207a2d013c0c89e197bbaae3c" +dependencies = [ + "hickory-resolver", + "rama-core", + "rama-net", + "rama-utils", + "serde", + "tokio", +] + +[[package]] +name = "rama-error" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b3236ea868a50937223cbe56130b99725235c001ca8886cc01bf599736c9766" + +[[package]] +name = "rama-http" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52279e9b6c364ac8db44209707544eac45a3b79c1953cb3cd3985272565d9818" +dependencies = [ + "async-compression", + "bitflags", + "chrono", + "const_format", + "csv", + "flate2", + "http-range-header", + "httpdate", + "iri-string", + "matchit", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "rama-core", + "rama-error", + "rama-http-headers", + "rama-http-types", + "rama-net", + "rama-ua", + "rama-utils", + "rand 0.9.1", + "rawzip", + "regex", + "serde", + "serde_html_form", + "serde_json", + "smol_str", + "tokio", + "tokio-util", + "uuid", +] + +[[package]] +name = "rama-http-backend" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f53a4fab6c60854a5d53dccdfdec7b86be697269d523adfff3ff13cca7455a9" +dependencies = [ + "const_format", + "futures", + "h2", + "pin-project-lite", + "rama-core", + "rama-http", + "rama-http-core", + "rama-http-headers", + "rama-http-types", + "rama-net", + "rama-tcp", + "rama-unix", + "rama-utils", + "tokio", +] + +[[package]] +name = "rama-http-core" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d2a4dc0b90d0ff7a2b7eda9993b51c7ad234b343c700f480ff83f7dea46966" +dependencies = [ + "atomic-waker", + "futures-channel", + "httparse", + "httpdate", + "indexmap", + "itoa", + "pin-project-lite", + "rama-core", + "rama-http", + "rama-http-types", + "rama-net", + "slab", + "smallvec", + "tokio", + "tokio-test", + "tokio-util", + "want", +] + +[[package]] +name = "rama-http-headers" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c49d5a5f7c95eaffab07609767ed60d52f6c56e7c2115565311545e7141ecd23" +dependencies = [ + "base64 0.22.1", + "httpdate", + "mime", + "rama-core", + "rama-error", + "rama-http-types", + "rama-macros", + "rama-net", + "rama-utils", + "rand 0.9.1", + "serde", + "sha1", +] + +[[package]] +name = "rama-http-types" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020137823b0d45de8eb78d261a45dc92a6d2cf763a948fc01ff922cd23eeabd3" +dependencies = [ + "const_format", + "fnv", + "http", + "http-body", + "http-body-util", + "itoa", + "memchr", + "mime", + "mime_guess", + "nom 8.0.0", + "pin-project-lite", + "rama-core", + "rama-error", + "rama-macros", + "rama-utils", + "rand 0.9.1", + "serde", + "serde_json", + "smallvec", + "smol_str", + "sync_wrapper", + "tokio", + "tokio-util", +] + +[[package]] +name = "rama-macros" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8531184c892978544f6799b132e2ff7c00ceaf8b7d68c2d693ad441d351cbf5" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", +] + +[[package]] +name = "rama-net" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2ca3d7c32de50880b81fc7d923fa3d9c9cf8d89c6557abdbd7e58758087926" +dependencies = [ + "const_format", + "flume", + "hex", + "ipnet", + "itertools 0.14.0", + "md5", + "nom 8.0.0", + "parking_lot", + "pin-project-lite", + "psl", + "radix_trie", + "rama-core", + "rama-http-types", + "rama-macros", + "rama-utils", + "serde", + "sha2 0.10.9", + "smol_str", + "socket2 0.6.1", + "tokio", +] + +[[package]] +name = "rama-socks5" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aeb01a3889de2d2a30e2a9143ffd26e1ed805f04fc477b02883dc0b907533b2" +dependencies = [ + "byteorder", + "rama-core", + "rama-dns", + "rama-net", + "rama-tcp", + "rama-udp", + "rama-utils", + "rand 0.9.1", + "smallvec", + "tokio", +] + +[[package]] +name = "rama-tcp" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f6b443f656ccf6d4d3cf5e288f73d5484f92c17dbe9836a1cdf13b4c9a4f67" +dependencies = [ + "rama-core", + "rama-dns", + "rama-http-types", + "rama-net", + "rama-utils", + "rand 0.9.1", + "tokio", +] + +[[package]] +name = "rama-tls-boring" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed51b8ae893be304f578b94702f92d564e5d293928349e92d6151d9ab78bf90" +dependencies = [ + "brotli", + "flate2", + "flume", + "itertools 0.14.0", + "moka", + "parking_lot", + "pin-project-lite", + "rama-boring", + "rama-boring-tokio", + "rama-core", + "rama-http-types", + "rama-net", + "rama-ua", + "rama-utils", + "schannel", + "tokio", + "zstd", +] + +[[package]] +name = "rama-tower" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2236eed00294ddc68befaaf565e6924d1918c97eaf63d71e2448d6ab961d52c" +dependencies = [ + "rama-core", + "rama-http-types", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "rama-ua" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496c2926de78dd36d3190fe852b11e39260079b05590c531b187c997078818c" +dependencies = [ + "itertools 0.14.0", + "rama-core", + "rama-http-headers", + "rama-http-types", + "rama-net", + "rama-utils", + "rand 0.9.1", + "serde", + "serde_json", +] + +[[package]] +name = "rama-udp" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0460c8db3b802ffa1d1f91bd375ee431da3780d6625d2503891e881ab115339a" +dependencies = [ + "rama-core", + "rama-net", + "tokio", + "tokio-util", +] + +[[package]] +name = "rama-unix" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa5e3efecbd7ba29667df34c9a949ed36da1d7b4f8f7b43b3b45e6234bbd932f" +dependencies = [ + "rama-core", + "rama-net", + "tokio", + "tokio-util", +] + +[[package]] +name = "rama-utils" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fd73071ddb078866fb7fca5def45bbfa12e7ea1cd819a0e3b701bf09ba2c72" +dependencies = [ + "parking_lot", + "pin-project-lite", + "rama-macros", + "serde", + "smol_str", + "tokio", +] + +[[package]] +name = "rama-ws" +version = "0.3.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6d2cc376163a79bdb2b49e8caad99b6b9c204424f8307c52b04c6627fd8e0b" +dependencies = [ + "flate2", + "rama-core", + "rama-http", + "rama-net", + "rama-utils", + "rand 0.9.1", + "tokio", +] + [[package]] name = "rand" version = "0.8.5" @@ -3677,6 +4423,26 @@ dependencies = [ "bitflags", ] +[[package]] +name = "rawzip" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27cc19f82b641448e861623f52a6a6413bbc0595b62a9d45bf31ccdf18aab72c" + +[[package]] +name = "rcgen" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f445eeec11f11c605386c9d319412b301de74cb0fa5b6b87e3004f5f0816938" +dependencies = [ + "aws-lc-rs", + "pem", + "rustls-pki-types", + "time", + "x509-parser 0.17.0", + "yasna", +] + [[package]] name = "redis" version = "1.0.0" @@ -3812,7 +4578,7 @@ dependencies = [ "futures-core", "futures-timer", "mime", - "nom", + "nom 7.1.3", "pin-project-lite", "reqwest", "thiserror 1.0.69", @@ -3858,18 +4624,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -3896,16 +4650,12 @@ dependencies = [ ] [[package]] -name = "rustix" -version = "0.38.44" +name = "rusticata-macros" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ - "bitflags", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", + "nom 7.1.3", ] [[package]] @@ -3917,7 +4667,7 @@ dependencies = [ "bitflags", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys", "windows-sys 0.59.0", ] @@ -3998,6 +4748,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -4072,6 +4828,19 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_html_form" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" +dependencies = [ + "form_urlencoded", + "indexmap", + "itoa", + "ryu", + "serde_core", +] + [[package]] name = "serde_json" version = "1.0.140" @@ -4250,6 +5019,16 @@ dependencies = [ "serde", ] +[[package]] +name = "smol_str" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3498b0a27f93ef1402f20eefacfaa1691272ac4eca1cdc8c596cb0a245d6cbf5" +dependencies = [ + "borsh", + "serde_core", +] + [[package]] name = "socket2" version = "0.5.10" @@ -4587,7 +5366,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix", "windows-sys 0.59.0", ] @@ -4708,27 +5487,39 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.10", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-graceful" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45740b38b48641855471cd402922e89156bdfbd97b69b45eeff170369cc18c7d" +dependencies = [ + "loom", + "pin-project-lite", + "slab", + "tokio", + "tracing", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -4803,6 +5594,19 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-test" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", +] + [[package]] name = "tokio-tungstenite" version = "0.28.0" @@ -4851,6 +5655,18 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.23.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + [[package]] name = "toml_parser" version = "1.0.4" @@ -5108,13 +5924,13 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.3", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] @@ -5318,18 +6134,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", -] - [[package]] name = "whoami" version = "1.6.0" @@ -5490,6 +6294,15 @@ dependencies = [ "windows-targets 0.53.5", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -5719,12 +6532,56 @@ dependencies = [ "tap", ] +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "ring", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "x509-parser" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3e137310115a65136898d2079f003ce33331a6c4b0d51f1531d1be082b6425" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom 7.1.3", + "oid-registry", + "rusticata-macros", + "thiserror 2.0.12", + "time", +] + [[package]] name = "xxhash-rust" version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + [[package]] name = "yoke" version = "0.8.0" diff --git a/examples/serve-with-hyper-rustls/Cargo.toml b/examples/serve-with-hyper-rustls/Cargo.toml new file mode 100644 index 0000000000..8e92732a66 --- /dev/null +++ b/examples/serve-with-hyper-rustls/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "example-serve-with-hyper-rustls" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +anyhow = "1.0" +axum = { path = "../../axum", features = ["http1", "http2"] } +http-body = "1.0" +hyper = "1.0" +rustls = "0.23" +rustls-pemfile = "2" +tokio = { version = "1.0", features = ["full"] } +tokio-rustls = { version = "0.26", default-features = false } +tokio-util = "0.7" +tower = { version = "0.5.2", features = ["util"] } +tracing = "0.1.41" diff --git a/examples/serve-with-hyper-rustls/self_signed_certs/cert.pem b/examples/serve-with-hyper-rustls/self_signed_certs/cert.pem new file mode 100644 index 0000000000..656aa88055 --- /dev/null +++ b/examples/serve-with-hyper-rustls/self_signed_certs/cert.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL +BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X +DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 +IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 +daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 +kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq +dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT +bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 +J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK +NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 +yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W +ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU +XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg ++MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 +Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 +fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr +AopvZ09uEQ== +-----END CERTIFICATE----- diff --git a/examples/serve-with-hyper-rustls/self_signed_certs/key.pem b/examples/serve-with-hyper-rustls/self_signed_certs/key.pem new file mode 100644 index 0000000000..3de14eb32f --- /dev/null +++ b/examples/serve-with-hyper-rustls/self_signed_certs/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD +BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS +tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw +RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l +YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t +HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV +W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB +12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI ++2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw +zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt +fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty +RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT +ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 +myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ +XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY +5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD +ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD +ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 +k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 +wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV +5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg +3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa +Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C +rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m +y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW +w37pCS7ykL+7gp7V0WShYsw= +-----END PRIVATE KEY----- diff --git a/examples/serve-with-hyper-rustls/src/main.rs b/examples/serve-with-hyper-rustls/src/main.rs new file mode 100644 index 0000000000..c0cd650698 --- /dev/null +++ b/examples/serve-with-hyper-rustls/src/main.rs @@ -0,0 +1,143 @@ +//! Run with +//! +//! ```not_rust +//! cargo run -p example-serve-with-hyper-rustls +//! ``` +//! +//! Test that the server runs with +//! ```bash +//! curl -kv https://localhost:3000 +//! ``` + +use std::convert::Infallible; +use std::error::Error as StdError; +use std::future::poll_fn; +use std::net::{Ipv4Addr, SocketAddr}; +use std::pin::pin; +use std::sync::Arc; +use std::{fs, io}; + +use axum::response::Response; +use axum::serve::{Connection, ConnectionBuilder, Hyper}; +use axum::{extract::Request, routing::get, Router}; +use http_body::Body as HttpBody; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use rustls::ServerConfig; +use tokio::io::{AsyncRead, AsyncWrite}; +use tokio::net::TcpListener; +use tokio_rustls::TlsAcceptor; +use tower::Service; + +#[derive(Clone)] +pub struct HyperRustls { + tls_acceptor: TlsAcceptor, + inner: Hyper, +} + +impl HyperRustls { + pub fn try_new() -> anyhow::Result { + // Load public certificate. + let certs = load_certs(&format!( + "{}/self_signed_certs/cert.pem", + env!("CARGO_MANIFEST_DIR") + ))?; + // Load private key. + let key = load_private_key(&format!( + "{}/self_signed_certs/key.pem", + env!("CARGO_MANIFEST_DIR") + ))?; + + // Build TLS configuration. + let mut server_config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(certs, key) + .inspect_err(|e| tracing::error!(error = display(e), "Cannot load certificate.")) + .unwrap(); + server_config.alpn_protocols = + vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]; + + Ok(Self { + tls_acceptor: TlsAcceptor::from(Arc::new(server_config)), + inner: Hyper::default(), + }) + } +} + +impl ConnectionBuilder for HyperRustls +where + Io: AsyncRead + AsyncWrite + Unpin + Send + 'static, + S: Service, Error = Infallible> + Clone + Send + 'static, + >::Future: Send, + B: HttpBody + Send + 'static, + B::Data: Send, + B::Error: Into>, +{ + fn build_connection(&mut self, io: Io, service: S) -> impl Connection { + let tls_acceptor = self.tls_acceptor.clone(); + let mut hyper = self.inner.clone(); + + Box::pin(async move { + let tls_stream = match tls_acceptor.accept(io).await { + Ok(tls_stream) => tls_stream, + Err(err) => { + eprintln!("failed to perform tls handshake: {err:#}"); + return Err(Box::new(err) as _); + } + }; + + let mut connection = pin!(hyper.build_connection(tls_stream, service)); + + poll_fn(|cx| connection.as_mut().poll_connection(cx)).await + }) + } + + fn graceful_shutdown(&mut self) { + ConnectionBuilder::::graceful_shutdown(&mut self.inner); + } +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 3000); + + println!("Starting to serve on https://{addr}"); + + // Create a regular axum app. + let app = Router::new().route("/", get(|| async { "Hello!" })); + + // Create a `TcpListener` using tokio. + let listener = TcpListener::bind(addr).await.unwrap(); + + // Create a connection builder which first drives the TLS handshake and then uses `hyper` to + // serve the connection. + let connection_builder = HyperRustls::try_new()?; + + axum::serve::serve(listener, app) + .with_connection_builder(connection_builder) + .await + .unwrap(); + + Ok(()) +} + +// Load public certificate from file. +fn load_certs(filename: &str) -> io::Result>> { + // Open certificate file. + let certfile = fs::File::open(filename) + .map_err(|e| io::Error::other(format!("failed to open {filename}: {e}")))?; + let mut reader = io::BufReader::new(certfile); + + // Load and return certificate. + rustls_pemfile::certs(&mut reader).collect() +} + +// Load private key from file. +fn load_private_key(filename: &str) -> io::Result> { + // Open keyfile. + let keyfile = fs::File::open(filename) + .map_err(|e| io::Error::other(format!("failed to open {filename}: {e}")))?; + let mut reader = io::BufReader::new(keyfile); + + // Load and return a single private key. + rustls_pemfile::private_key(&mut reader).map(|key| key.unwrap()) +} diff --git a/examples/serve-with-rama/Cargo.toml b/examples/serve-with-rama/Cargo.toml new file mode 100644 index 0000000000..17eca43021 --- /dev/null +++ b/examples/serve-with-rama/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "example-serve-with-rama" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +axum = { path = "../../axum" } +pin-project-lite = "0.2.7" +rama = { version = "0.3.0-alpha.3", features = ["http-full", "tower"], default-features = false } +tokio = { version = "1.0", features = ["full"] } +tower = { version = "0.5.2", features = ["util"] } diff --git a/examples/serve-with-rama/src/main.rs b/examples/serve-with-rama/src/main.rs new file mode 100644 index 0000000000..a3e53d8e53 --- /dev/null +++ b/examples/serve-with-rama/src/main.rs @@ -0,0 +1,141 @@ +//! Run with +//! +//! ```not_rust +//! cargo run -p example-serve-with-rama +//! ``` +//! +//! This example shows how to run axum using rama as the HTTP driving server instead of the default +//! hyper. +use std::convert::Infallible; +use std::future::{ready, Future}; + +use axum::body::{Body as AxumBody, HttpBody}; +use axum::http::StatusCode; +use axum::response::Response; +use axum::serve::{Connection, ConnectionBuilder}; +use axum::{extract::Request, routing::get, Router}; +use pin_project_lite::pin_project; +use rama::graceful::ShutdownBuilder; +use rama::http::core::body::Frame; +use rama::http::core::server::conn::auto; +use rama::http::Body as RamaBody; +use rama::rt::Executor; +use rama::utils::tower::ServiceAdapter; +use rama::Context; +use tokio::sync::watch; +use tower::{Service, ServiceExt}; + +#[tokio::main] +async fn main() { + let app = Router::new().route("/", get(|| ready(StatusCode::IM_A_TEAPOT))); + + let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") + .await + .unwrap(); + let connection_builder = RamaConnectionBuilder::new(); + println!("listening on {}", listener.local_addr().unwrap()); + axum::serve::serve(listener, app) + .with_connection_builder(connection_builder) + .await + .unwrap(); +} + +#[derive(Clone)] +pub struct RamaConnectionBuilder { + server: rama::http::server::HttpServer, + shutdown: Option>, +} + +pin_project! { + pub struct RamaConnection { + #[pin] + inner: F + } +} + +pin_project! { + pub struct SyncBody { + #[pin] + inner: B, + } +} + +// SAFETY +// This is fine because we never provide references to the inner body and the only publicly +// accessible method using it requires `Pin<&mut Self>` which means exclusive access. +unsafe impl Sync for SyncBody {} + +impl SyncBody { + pub fn new(body: B) -> Self { + Self { inner: body } + } +} + +impl HttpBody for SyncBody { + type Data = B::Data; + + type Error = B::Error; + + fn poll_frame( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll, Self::Error>>> { + self.project().inner.poll_frame(cx) + } + + // We must NOT delegate the provided methods to the inner body. We must NOT use references to + // the inner body as it may not be `Sync` and who knows what thread holds references to it. +} + +impl RamaConnectionBuilder { + fn new() -> Self { + let (shutdown_tx, shutdown_rx) = watch::channel(()); + let shutdown = ShutdownBuilder::new() + .with_signal(async move { shutdown_tx.closed().await }) + .build(); + Self { + server: rama::http::server::HttpServer::auto(Executor::graceful(shutdown.guard())), + shutdown: Some(shutdown_rx), + } + } +} + +impl ConnectionBuilder for RamaConnectionBuilder +where + Io: rama::net::stream::Stream, + S: Clone + Send + Sync + 'static, + S: Service + + Clone + + Send + + Sync + + 'static, +{ + fn build_connection(&mut self, io: Io, service: S) -> impl Connection { + let rama_service = ServiceAdapter::new( + service + .map_request(|request: Request| request.map(AxumBody::new)) + .map_response(|response: Response| { + response.map(SyncBody::new).map(RamaBody::new) + }), + ); + RamaConnection { + inner: self.server.serve(Context::default(), io, rama_service), + } + } + + fn graceful_shutdown(&mut self) { + self.shutdown.take(); + } +} + +impl Connection for RamaConnection +where + F: Future>> + Send, +{ + fn poll_connection( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll>> { + self.project().inner.poll(cx) + } +}