use std::future::Future; use actix_web::body::{BoxBody, MessageBody}; use actix_web::dev::{ServiceRequest, ServiceResponse}; use actix_web::http::header::{HeaderName, HeaderValue}; use actix_web::middleware::Next; use actix_web::{Error, HttpMessage}; use uuid::Uuid; pub use envelope::context::resolve_request_id; pub const REQUEST_ID_HEADER: HeaderName = HeaderName::from_static("x-request-id"); #[derive(Debug, Clone)] pub struct RequestId(pub String); /// The request id of the request currently being handled, when one is set. pub fn request_id() -> Option { envelope::context::request_id() } /// Runs `future` with `request_id` available to nested code, so queue /// consumers keep the id of the request that produced the message. pub async fn with_request_id(request_id: Option<&str>, future: F) -> F::Output { match request_id { Some(request_id) => { envelope::context::with_request_id(request_id.to_string(), future).await } None => future.await, } } pub fn new_request_id() -> String { Uuid::now_v7().to_string() } pub async fn trace_request( request: ServiceRequest, next: Next, ) -> Result, Error> where B: MessageBody + 'static, { let request_id = request .extensions() .get::<::session::RequestId>() .map(|value| value.0.clone()) .or_else(|| ::session::validate_request_id(header(&request))) .unwrap_or_else(new_request_id); request .extensions_mut() .insert(RequestId(request_id.clone())); request .extensions_mut() .insert(::session::RequestId(request_id.clone())); let response = envelope::context::with_request_id(request_id.clone(), next.call(request)).await?; let mut response = response.map_into_boxed_body(); if let Ok(value) = HeaderValue::try_from(request_id.as_str()) { response.headers_mut().insert(REQUEST_ID_HEADER, value); } Ok(response) } fn header(request: &ServiceRequest) -> Option<&str> { request .headers() .get(&REQUEST_ID_HEADER) .and_then(|value| value.to_str().ok()) }