| use super::future::ResponseFuture; |
| use crate::{util::ServiceExt, BoxError}; |
| use futures_core::ready; |
| use futures_util::future::TryFutureExt; |
| use std::{ |
| future::Future, |
| pin::Pin, |
| task::{Context, Poll}, |
| }; |
| use tower_service::Service; |
| use tracing::Instrument; |
| |
| /// Spawns tasks to drive an inner service to readiness. |
| /// |
| /// See crate level documentation for more details. |
| #[derive(Debug)] |
| pub struct SpawnReady<S> { |
| inner: Inner<S>, |
| } |
| |
| #[derive(Debug)] |
| enum Inner<S> { |
| Service(Option<S>), |
| Future(tokio::task::JoinHandle<Result<S, BoxError>>), |
| } |
| |
| impl<S> SpawnReady<S> { |
| /// Creates a new [`SpawnReady`] wrapping `service`. |
| pub fn new(service: S) -> Self { |
| Self { |
| inner: Inner::Service(Some(service)), |
| } |
| } |
| } |
| |
| impl<S> Drop for SpawnReady<S> { |
| fn drop(&mut self) { |
| if let Inner::Future(ref mut task) = self.inner { |
| task.abort(); |
| } |
| } |
| } |
| |
| impl<S, Req> Service<Req> for SpawnReady<S> |
| where |
| Req: 'static, |
| S: Service<Req> + Send + 'static, |
| S::Error: Into<BoxError>, |
| { |
| type Response = S::Response; |
| type Error = BoxError; |
| type Future = ResponseFuture<S::Future, S::Error>; |
| |
| fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), BoxError>> { |
| loop { |
| self.inner = match self.inner { |
| Inner::Service(ref mut svc) => { |
| if let Poll::Ready(r) = svc.as_mut().expect("illegal state").poll_ready(cx) { |
| return Poll::Ready(r.map_err(Into::into)); |
| } |
| |
| let svc = svc.take().expect("illegal state"); |
| let rx = |
| tokio::spawn(svc.ready_oneshot().map_err(Into::into).in_current_span()); |
| Inner::Future(rx) |
| } |
| Inner::Future(ref mut fut) => { |
| let svc = ready!(Pin::new(fut).poll(cx))??; |
| Inner::Service(Some(svc)) |
| } |
| } |
| } |
| } |
| |
| fn call(&mut self, request: Req) -> Self::Future { |
| match self.inner { |
| Inner::Service(Some(ref mut svc)) => { |
| ResponseFuture::new(svc.call(request).map_err(Into::into)) |
| } |
| _ => unreachable!("poll_ready must be called"), |
| } |
| } |
| } |