diff --git a/Cargo.lock b/Cargo.lock index a57d38df..e00f9374 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -540,45 +540,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f9214f3e703236b221f1a9cd88ec8b4adfa5296de01ab96216361f4692f56" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "hyper-util" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca339002caeb0d159cc6e023dff48e199f081e42fa039895c7c6f38b37f2e9d" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "pin-project-lite", - "socket2", - "tokio", - "tower", - "tower-service", - "tracing", -] - [[package]] name = "indexmap" version = "2.1.0" @@ -616,9 +577,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -678,9 +639,9 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "9376a4f0340565ad675d11fc1419227faf5f60cd7ac9cb2e7185a471f30af833" [[package]] name = "memchr" @@ -882,10 +843,10 @@ dependencies = [ "http-body", "http-body-util", "httparse", - "hyper", - "hyper-util", "matchit", "pin-project-lite", + "rama-hyper", + "rama-hyper-util", "rcgen", "rustls", "rustls-pki-types", @@ -897,7 +858,6 @@ dependencies = [ "tokio-test", "tower-async", "tower-async-http", - "tower-async-hyper", "tracing", "tracing-subscriber", ] @@ -906,6 +866,46 @@ dependencies = [ name = "rama-cli" version = "0.2.0" +[[package]] +name = "rama-hyper" +version = "0.1000001.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc00922b75a92322e2ed442e63b2b0ee3f89cdd6c3bc5b03ecf55b934ec55716" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "want", +] + +[[package]] +name = "rama-hyper-util" +version = "0.1001.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496bf56d0c08f83ccc84f3c96171a3449739188ae23e4f794039c44c837d12a4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "rama-hyper", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "rama-rt" version = "0.2.0" @@ -1372,18 +1372,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "tower-async-hyper" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97e4dad66c0a36994e59b8d74b15e0b9b9ef35c85cb150e9f3560bb6001ec59f" -dependencies = [ - "http-body", - "hyper", - "pin-project-lite", - "tower-async-service", -] - [[package]] name = "tower-async-layer" version = "0.2.0" @@ -1470,6 +1458,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "typenum" version = "1.17.0" @@ -1524,6 +1518,15 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1532,9 +1535,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1542,9 +1545,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -1557,9 +1560,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1567,9 +1570,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -1580,9 +1583,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" diff --git a/Cargo.toml b/Cargo.toml index fcd3fa81..5f950ac4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,9 @@ http = "1" http-body = "1" http-body-util = "0.1" httparse = "1.8" -hyper = { version = "1", features = ["http1", "http2", "server"] } -hyper-util = { version = "0.1", features = ["tokio", "server-auto"] } -matchit = "0.7" +hyper = { package = "rama-hyper", version = "0.1000001", features = ["http1", "http2", "server"] } +hyper-util = { package = "rama-hyper-util", version = "0.1001", features = ["tokio", "server-auto"] } +matchit = "0.4.2" pin-project-lite = "0.2.13" rustls = "0.22.0-alpha.3" serde = { version = "1.0", features = ["derive"] } @@ -44,7 +44,6 @@ tower-async = { version = "0.2", features = [ "limit", ] } tower-async-http = { version = "0.2", features = ["full"] } -tower-async-hyper = "0.1" tracing = "0.1" [dev-dependencies] diff --git a/src/service/hyper/body.rs b/src/service/hyper/body.rs new file mode 100644 index 00000000..8ce50622 --- /dev/null +++ b/src/service/hyper/body.rs @@ -0,0 +1,82 @@ +use std::{ + pin::Pin, + task::{Context, Poll}, +}; + +use http_body::{Body as HttpBody, Frame, SizeHint}; +use hyper::body::Incoming; + +pin_project_lite::pin_project! { + /// A wrapper around `hyper::body::Incoming` that implements `http_body::Body`. + /// + /// This type is used to bridge the `hyper` and `tower-async` ecosystems. + /// Reason is that a lot of middlewares in `tower-async-http` that + /// operate on `http_body::Body` which also have to implement `Default`. + #[derive(Debug, Default)] + pub struct Body { + #[pin] + inner: Option, + } +} + +impl From for Body { + fn from(inner: Incoming) -> Self { + Self { inner: Some(inner) } + } +} + +impl Body { + /// Return a reference to the inner [`hyper::body::Incoming`] value. + /// + /// This is normally not needed, + /// but in case you do ever need it, it's here. + pub fn as_ref(&self) -> Option<&Incoming> { + self.inner.as_ref() + } + + /// Return a mutable reference to the inner [`hyper::body::Incoming`] value. + /// + /// This is normally not needed, + /// but in case you do ever need it, it's here. + pub fn as_mut(&mut self) -> Option<&mut Incoming> { + self.inner.as_mut() + } + + /// Turn this [`Body`] into the inner [`hyper::body::Incoming`] value. + /// + /// This is normally not needed, + /// but in case you do ever need it, it's here. + pub fn into_inner(self) -> Option { + self.inner + } +} + +impl HttpBody for Body { + type Data = ::Data; + type Error = ::Error; + + fn poll_frame( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll, Self::Error>>> { + self.project() + .inner + .as_pin_mut() + .map(|incoming| incoming.poll_frame(cx)) + .unwrap_or_else(|| Poll::Ready(None)) + } + + fn is_end_stream(&self) -> bool { + self.inner + .as_ref() + .map(|incoming| incoming.is_end_stream()) + .unwrap_or(true) + } + + fn size_hint(&self) -> SizeHint { + self.inner + .as_ref() + .map(|incoming| incoming.size_hint()) + .unwrap_or_default() + } +} diff --git a/src/service/hyper/mod.rs b/src/service/hyper/mod.rs new file mode 100644 index 00000000..2f143c61 --- /dev/null +++ b/src/service/hyper/mod.rs @@ -0,0 +1,7 @@ +//! Bridges a `tower-async` `Service` to be used within a `hyper` (1.x) environment. + +mod service; +pub use service::{BoxFuture, HyperServiceWrapper, TowerHyperServiceExt}; + +mod body; +pub use body::Body as HyperBody; diff --git a/src/service/hyper/service.rs b/src/service/hyper/service.rs new file mode 100644 index 00000000..3a386b5d --- /dev/null +++ b/src/service/hyper/service.rs @@ -0,0 +1,103 @@ +use std::pin::Pin; +use std::sync::Arc; + +use hyper::service::Service as HyperService; + +use tower_async::Service; + +/// Trait to convert a [`tower_async::Service`] into a [`hyper::service::Service`]. +/// +/// [`tower_async::Service`]: https://docs.rs/tower-async/latest/tower_async/trait.Service.html +/// [`hyper::service::Service`]: https://docs.rs/hyper/latest/hyper/service/trait.Service.html +pub trait TowerHyperServiceExt { + /// Convert a [`tower_async::Service`] into a [`hyper::service::Service`]. + /// + /// [`tower_async::Service`]: https://docs.rs/tower-async/latest/tower_async/trait.Service.html + /// [`hyper::service::Service`]: https://docs.rs/hyper/latest/hyper/service/trait.Service.html + fn into_hyper_service(self) -> HyperServiceWrapper; +} + +impl TowerHyperServiceExt for S +where + S: Service, +{ + fn into_hyper_service(self) -> HyperServiceWrapper { + HyperServiceWrapper { + service: Arc::new(self), + } + } +} + +#[derive(Debug, Clone)] +/// A wrapper around a [`tower_async::Service`] that implements [`hyper::service::Service`]. +/// +/// [`tower_async::Service`]: https://docs.rs/tower-async/latest/tower_async/trait.Service.html +/// [`hyper::service::Service`]: https://docs.rs/hyper/latest/hyper/service/trait.Service.html +pub struct HyperServiceWrapper { + service: Arc, +} + +impl HyperService for HyperServiceWrapper +where + S: Service + Send + Sync + 'static, + Request: Send + 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = BoxFuture<'static, Result>; + + fn call(&self, req: Request) -> Self::Future { + let service = self.service.clone(); + let fut = async move { service.call(req).await }; + Box::pin(fut) + } +} + +pub type BoxFuture<'a, T> = Pin + Send + 'a>>; + +#[cfg(test)] +mod test { + use std::convert::Infallible; + + use super::*; + + fn require_send(t: T) -> T { + t + } + + fn require_service>(s: S) -> S { + s + } + + #[tokio::test] + async fn test_into_hyper_service() { + let service = + tower_async::service_fn(|req: &'static str| async move { Ok::<_, Infallible>(req) }); + let service = require_service(service); + let hyper_service = service.into_hyper_service(); + inner_test_hyper_service(hyper_service).await; + } + + #[tokio::test] + async fn test_into_layered_hyper_service() { + let service = tower_async::ServiceBuilder::new() + .timeout(std::time::Duration::from_secs(5)) + .service_fn(|req: &'static str| async move { Ok::<_, Infallible>(req) }); + let service = require_service(service); + let hyper_service = service.into_hyper_service(); + inner_test_hyper_service(hyper_service).await; + } + + async fn inner_test_hyper_service(hyper_service: H) + where + H: HyperService<&'static str, Response = &'static str>, + H::Error: std::fmt::Debug, + H::Future: Send, + { + let fut = hyper_service.call("hello"); + let fut = require_send(fut); + + let res = fut.await.expect("call hyper service"); + assert_eq!(res, "hello"); + } +} diff --git a/src/service/mod.rs b/src/service/mod.rs index fe23699e..fc4ce644 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -65,11 +65,5 @@ pub mod limit { } pub mod http; - -pub mod hyper { - //! Bridges a `tower-async` `Service` to be used within a `hyper` (1.x) environment. - - pub use tower_async_hyper::{BoxFuture, HyperBody, HyperServiceWrapper, TowerHyperServiceExt}; -} - +pub mod hyper; pub mod spawn;