use super::{REQUEST_ID_HEADER, SERVER_TIME_HEADER}; use axum::{extract::Request, response::Response}; use std::{ future::Future, pin::Pin, task::{Context, Poll}, }; use tokio::time::Instant; use tower::{Layer, Service}; use tracing::warn; #[derive(Clone)] pub struct ServerTimeLayer; impl Layer for ServerTimeLayer { type Service = ServerTimeMiddleware; fn layer(&self, inner: S) -> Self::Service { ServerTimeMiddleware { inner } } } #[derive(Clone)] pub struct ServerTimeMiddleware { inner: S, } impl Service for ServerTimeMiddleware where S: Service + Send + 'static, S::Future: Send + 'static, { type Response = S::Response; type Error = S::Error; // `BoxFuture` is a type alias for `Pin>` type Future = Pin> + Send + 'static>>; fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { self.inner.poll_ready(cx) } fn call(&mut self, request: Request) -> Self::Future { let start = Instant::now(); let future = self.inner.call(request); Box::pin(async move { let mut res: Response = future.await?; let elapsed = format!("{}us", start.elapsed().as_micros()); match elapsed.parse() { Ok(v) => { res.headers_mut().insert(SERVER_TIME_HEADER, v); } Err(e) => { warn!( "Parse elapsed time failed: {} for request {:?}", e, res.headers().get(REQUEST_ID_HEADER) ); } } Ok(res) }) } }