| use futures_util::ready; | |
| use pin_project_lite::pin_project; | |
| use std::time::Duration; | |
| use std::{ | |
| future::Future, | |
| pin::Pin, | |
| task::{Context, Poll}, | |
| }; | |
| use tokio::time::Instant; | |
| use tower_service::Service; | |
| /// Record is the interface for accepting request latency measurements. When | |
| /// a request completes, record is called with the elapsed duration between | |
| /// when the service was called and when the future completed. | |
| pub trait Record { | |
| fn record(&mut self, latency: Duration); | |
| } | |
| /// Latency is a middleware that measures request latency and records it to the | |
| /// provided Record instance. | |
| #[derive(Clone, Debug)] | |
| pub struct Latency<R, S> { | |
| rec: R, | |
| service: S, | |
| } | |
| pin_project! { | |
| #[derive(Debug)] | |
| pub struct ResponseFuture<R, F> { | |
| start: Instant, | |
| rec: R, | |
| #[pin] | |
| inner: F, | |
| } | |
| } | |
| impl<S, R> Latency<R, S> | |
| where | |
| R: Record + Clone, | |
| { | |
| pub fn new<Request>(rec: R, service: S) -> Self | |
| where | |
| S: Service<Request>, | |
| S::Error: Into<crate::BoxError>, | |
| { | |
| Latency { rec, service } | |
| } | |
| } | |
| impl<S, R, Request> Service<Request> for Latency<R, S> | |
| where | |
| S: Service<Request>, | |
| S::Error: Into<crate::BoxError>, | |
| R: Record + Clone, | |
| { | |
| type Response = S::Response; | |
| type Error = crate::BoxError; | |
| type Future = ResponseFuture<R, S::Future>; | |
| fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | |
| self.service.poll_ready(cx).map_err(Into::into) | |
| } | |
| fn call(&mut self, request: Request) -> Self::Future { | |
| ResponseFuture { | |
| start: Instant::now(), | |
| rec: self.rec.clone(), | |
| inner: self.service.call(request), | |
| } | |
| } | |
| } | |
| impl<R, F, T, E> Future for ResponseFuture<R, F> | |
| where | |
| R: Record, | |
| F: Future<Output = Result<T, E>>, | |
| E: Into<crate::BoxError>, | |
| { | |
| type Output = Result<T, crate::BoxError>; | |
| fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | |
| let this = self.project(); | |
| let rsp = ready!(this.inner.poll(cx)).map_err(Into::into)?; | |
| let duration = Instant::now().saturating_duration_since(*this.start); | |
| this.rec.record(duration); | |
| Poll::Ready(Ok(rsp)) | |
| } | |
| } |