Skip to content
Clone
868bd23
61 lines 1.6 KB Rust
use std::future::{Ready, ready};
use actix_web::dev::Payload;
use actix_web::{FromRequest, HttpMessage, HttpRequest};
use uuid::Uuid;
pub const REQUEST_ID_HEADER: &str = "x-request-id";
pub const LEGACY_REQUEST_ID_HEADER: &str = "gitdata-request-id";
pub const REQUEST_ID_MAX_LEN: usize = 64;
#[derive(Debug, Clone)]
pub struct RequestId(pub String);
impl RequestId {
pub fn from_header(value: Option<&str>) -> Self {
Self(
value
.and_then(validate_request_id)
.unwrap_or_else(new_request_id),
)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
pub fn validate_request_id(value: &str) -> Option<String> {
let value = value.trim();
if value.is_empty() || value.len() > REQUEST_ID_MAX_LEN {
return None;
}
let allowed = value
.bytes()
.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.' | b':'));
allowed.then(|| value.to_owned())
}
pub fn new_request_id() -> String {
Uuid::now_v7().to_string()
}
impl FromRequest for RequestId {
type Error = actix_web::Error;
type Future = Ready<Result<Self, Self::Error>>;
fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
let request_id = req
.extensions()
.get::<RequestId>()
.cloned()
.unwrap_or_else(|| {
RequestId::from_header(
req.headers()
.get(REQUEST_ID_HEADER)
.and_then(|value| value.to_str().ok()),
)
});
ready(Ok(request_id))
}
}