use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use http::HeaderName;
use http::HeaderValue;
use crate::middleware::IntoMiddleware;
use crate::middleware::Next;
use crate::types::Request;
use crate::types::Response;
#[derive(Debug, Clone)]
pub struct RequestIdValue(pub String);
pub struct RequestId {
header: HeaderName,
generator: Arc<dyn Fn() -> String + Send + Sync + 'static>,
}
impl Default for RequestId {
fn default() -> Self {
Self::new()
}
}
impl RequestId {
pub fn new() -> Self {
Self {
header: HeaderName::from_static("x-request-id"),
generator: Arc::new(|| {
use std::time::{SystemTime, UNIX_EPOCH};
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default();
let seed = now.as_nanos();
format!(
"{:08x}-{:04x}-4{:03x}-{:04x}-{:012x}",
(seed & 0xFFFFFFFF) as u32,
((seed >> 32) & 0xFFFF) as u16,
((seed >> 48) & 0x0FFF) as u16,
(0x8000 | ((seed >> 60) & 0x3FFF)) as u16,
(seed.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407) & 0xFFFFFFFFFFFF) as u64,
)
}),
}
}
pub fn header_name(mut self, name: &'static str) -> Self {
self.header = HeaderName::from_static(name);
self
}
pub fn generator(mut self, f: impl Fn() -> String + Send + Sync + 'static) -> Self {
self.generator = Arc::new(f);
self
}
}
impl IntoMiddleware for RequestId {
fn into_middleware(
self,
) -> impl Fn(Request, Next) -> Pin<Box<dyn Future<Output = Response> + Send + 'static>>
+ Clone
+ Send
+ Sync
+ 'static {
let header = self.header;
let generator = self.generator;
move |mut req: Request, next: Next| {
let header = header.clone();
let generator = generator.clone();
Box::pin(async move {
let id = req
.headers()
.get(&header)
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string())
.unwrap_or_else(|| generator());
req.extensions_mut().insert(RequestIdValue(id.clone()));
let mut resp = next.run(req).await;
if let Ok(val) = HeaderValue::from_str(&id) {
resp.headers_mut().insert(header, val);
}
resp
})
}
}
}