tork_core/middleware/
request_id.rs1use http::{HeaderName, HeaderValue};
4
5use crate::error::Result;
6use crate::middleware::{DuplicatePolicy, Middleware, Next, Request};
7use crate::response::Response;
8use crate::router::BoxFuture;
9
10const DEFAULT_HEADER: &str = "x-request-id";
12const REQUEST_ID_PREFIX: &str = "req-";
14
15pub struct RequestId {
22 header: HeaderName,
23}
24
25impl RequestId {
26 pub fn new() -> Self {
28 Self {
29 header: HeaderName::from_static(DEFAULT_HEADER),
30 }
31 }
32
33 pub fn header_name(mut self, name: &'static str) -> Self {
35 self.header = HeaderName::from_static(name);
36 self
37 }
38}
39
40impl Default for RequestId {
41 fn default() -> Self {
42 Self::new()
43 }
44}
45
46impl Middleware for RequestId {
47 fn handle(&self, mut request: Request, next: Next) -> BoxFuture<'static, Result<Response>> {
48 let header = self.header.clone();
49
50 let id = request
51 .headers()
52 .get(&header)
53 .and_then(|value| value.to_str().ok())
54 .map(str::to_owned)
55 .unwrap_or_else(|| format!("{REQUEST_ID_PREFIX}{}", uuid::Uuid::new_v4()));
56
57 if let Ok(value) = HeaderValue::from_str(&id) {
58 request.headers_mut().insert(header.clone(), value);
59 }
60
61 Box::pin(async move {
62 let mut response = next.run(request).await?;
63 if let Ok(value) = HeaderValue::from_str(&id) {
64 response.headers_mut().insert(header, value);
65 }
66 Ok(response)
67 })
68 }
69
70 fn name(&self) -> &'static str {
71 "RequestId"
72 }
73
74 fn duplicate_policy(&self) -> DuplicatePolicy {
75 DuplicatePolicy::Reject
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn header_name_builder_replaces_default_header() {
85 let request_id = RequestId::new().header_name("x-correlation-id");
86 assert_eq!(
87 request_id.header,
88 HeaderName::from_static("x-correlation-id")
89 );
90 }
91}