use axum::{
extract::Request,
http::{header, HeaderValue},
middleware::Next,
response::Response,
};
use tower::{Layer, Service};
use uuid::Uuid;
#[derive(Clone)]
pub struct RequestIdLayer;
impl RequestIdLayer {
pub fn new() -> Self {
Self
}
}
impl Default for RequestIdLayer {
fn default() -> Self {
Self::new()
}
}
impl<S> Layer<S> for RequestIdLayer {
type Service = RequestIdService<S>;
fn layer(&self, inner: S) -> Self::Service {
RequestIdService { inner }
}
}
#[derive(Clone)]
pub struct RequestIdService<S> {
inner: S,
}
impl<S> Service<Request> for RequestIdService<S>
where
S: Service<Request, Response = Response> + Send + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = std::pin::Pin<
Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>> + Send>,
>;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut req: Request) -> Self::Future {
let request_id = req
.headers()
.get("x-request-id")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string())
.unwrap_or_else(|| Uuid::new_v4().to_string());
req.extensions_mut().insert(request_id.clone());
let future = self.inner.call(req);
Box::pin(async move {
let mut response = future.await?;
if let Ok(header_value) = HeaderValue::from_str(&request_id) {
response
.headers_mut()
.insert("x-request-id", header_value);
}
Ok(response)
})
}
}