diff --git a/Cargo.toml b/Cargo.toml index 68b3a06..9cd3db8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,7 @@ futures-channel = { version = "0.3", optional = true } socket2 = { version = "0.5", optional = true, features = ["all"] } tracing = { version = "0.1", default-features = false, features = ["std"], optional = true } tokio = { version = "1", optional = true, default-features = false } -tower-service ={ version = "0.3", optional = true } -tower = { version = "0.4.1", optional = true, default-features = false, features = ["make", "util"] } +tower-service = { version = "0.3", optional = true } [dev-dependencies] hyper = { version = "1.4.0", features = ["full"] } @@ -58,14 +57,14 @@ full = [ "tokio", ] -client = ["hyper/client", "dep:tracing", "dep:futures-channel", "dep:tower", "dep:tower-service"] +client = ["hyper/client", "dep:tracing", "dep:futures-channel", "dep:tower-service"] client-legacy = ["client", "dep:socket2", "tokio/sync"] server = ["hyper/server"] server-auto = ["server", "http1", "http2"] -server-graceful = ["server", "tokio/sync"] +server-graceful = ["server", "tokio/sync", "futures-util/alloc"] -service = ["dep:tower", "dep:tower-service"] +service = ["dep:tower-service"] http1 = ["hyper/http1"] http2 = ["hyper/http2"] diff --git a/src/client/legacy/connect/mod.rs b/src/client/legacy/connect/mod.rs index e3369b5..d654515 100644 --- a/src/client/legacy/connect/mod.rs +++ b/src/client/legacy/connect/mod.rs @@ -343,8 +343,8 @@ pub(super) mod sealed { { type _Svc = S; - fn connect(self, _: Internal, dst: Uri) -> tower::util::Oneshot { - tower::util::Oneshot::new(self, dst) + fn connect(self, _: Internal, dst: Uri) -> crate::service::Oneshot { + crate::service::Oneshot::new(self, dst) } } @@ -357,10 +357,10 @@ pub(super) mod sealed { { type Connection = T; type Error = S::Error; - type Future = tower::util::Oneshot; + type Future = crate::service::Oneshot; fn connect(self, _: Internal, dst: Uri) -> Self::Future { - tower::util::Oneshot::new(self, dst) + crate::service::Oneshot::new(self, dst) } } diff --git a/src/service.rs b/src/service.rs index 4652a6a..c96af9e 100644 --- a/src/service.rs +++ b/src/service.rs @@ -6,7 +6,8 @@ use std::{ pin::Pin, task::{Context, Poll}, }; -use tower::{util::Oneshot, ServiceExt}; + +pub(crate) use self::internal::Oneshot; /// A tower service converted into a hyper service. #[derive(Debug, Copy, Clone)] @@ -33,7 +34,7 @@ where fn call(&self, req: R) -> Self::Future { TowerToHyperServiceFuture { - future: self.service.clone().oneshot(req), + future: Oneshot::new(self.service.clone(), req), } } } @@ -60,3 +61,66 @@ where self.project().future.poll(cx) } } + +mod internal { + use pin_project_lite::pin_project; + use std::future::Future; + use std::pin::Pin; + use std::task::{ready, Context, Poll}; + use tower_service::Service; + + // Not really pub, but used in a trait for bounds + pin_project! { + #[project = OneshotProj] + #[derive(Debug)] + pub enum Oneshot, Req> { + NotReady { + svc: S, + req: Option, + }, + Called { + #[pin] + fut: S::Future, + }, + Done, + } + } + + impl Oneshot + where + S: Service, + { + pub(crate) const fn new(svc: S, req: Req) -> Self { + Oneshot::NotReady { + svc, + req: Some(req), + } + } + } + + impl Future for Oneshot + where + S: Service, + { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + loop { + let this = self.as_mut().project(); + match this { + OneshotProj::NotReady { svc, req } => { + let _ = ready!(svc.poll_ready(cx))?; + let fut = svc.call(req.take().expect("already called")); + self.set(Oneshot::Called { fut }); + } + OneshotProj::Called { fut } => { + let res = ready!(fut.poll(cx))?; + self.set(Oneshot::Done); + return Poll::Ready(Ok(res)); + } + OneshotProj::Done => panic!("polled after complete"), + } + } + } + } +}