use http::{HeaderName, HeaderValue, Response};
use tokio::time::Instant;
use crate::service::{Layer, Service};
const SERVER_TIMING: HeaderName = HeaderName::from_static("server-timing");
pub struct ServerTimingHeaderLayer;
impl<S> Layer<S> for ServerTimingHeaderLayer {
type Service = ServerTimingHeaderService<S>;
fn layer(self, inner: S) -> Self::Service {
ServerTimingHeaderService { inner }
}
}
pub struct ServerTimingHeaderService<S> {
inner: S,
}
impl<S, R, B> Service<R> for ServerTimingHeaderService<S>
where
S: Service<R, Response = Response<B>> + Sync,
R: Send,
{
type Response = Response<B>;
async fn call(&self, req: R) -> Self::Response {
let start = Instant::now();
let mut response = self.inner.call(req).await;
let dur_micros = start.elapsed().as_micros() as u64;
let value = format!(
"server;dur={}.{}{}{}",
dur_micros / 1_000,
(dur_micros % 1_000) / 100,
(dur_micros % 100) / 10,
(dur_micros % 10),
);
let value = HeaderValue::try_from(value).unwrap();
response.headers_mut().insert(SERVER_TIMING, value);
response
}
}
#[cfg(test)]
mod test {
use std::time::Duration;
use tokio::time;
use crate::service::test_util::service_fn;
use super::*;
#[tokio::test(start_paused = true)]
async fn basics() {
let service = ServerTimingHeaderLayer.layer(service_fn(|delay| async move {
time::advance(delay).await;
Response::new(())
}));
let response = service.call(Duration::from_micros(1234)).await;
assert_eq!(
response.headers().get(SERVER_TIMING).unwrap(),
"server;dur=1.234"
);
}
}