request_id/
lib.rs

1//! A few common `MakeRequestId` implementations for use with `tower-http`
2
3#![warn(clippy::nursery)]
4#![warn(clippy::pedantic)]
5#![warn(clippy::cargo)]
6#![warn(missing_docs)]
7#![allow(clippy::needless_doctest_main)]
8
9use std::sync::{
10    atomic::{AtomicUsize, Ordering},
11    Arc,
12};
13
14use hyper::Request;
15use tower_http::request_id::{MakeRequestId, RequestId};
16
17#[cfg(feature = "uuid")]
18use uuid::Uuid;
19
20#[cfg(feature = "ulid")]
21use ulid::Ulid;
22
23/// A [`MakeRequestId`] that generates a [`RequestId`] from a [`Uuid`].
24///
25/// [`MakeRequestId`]: ::tower_http::request_id::MakeRequestId
26/// [`RequestId`]: ::tower_http::request_id::RequestId
27/// [`Uuid`]: ::uuid::Uuid
28#[derive(Clone, Copy, Debug, Default)]
29#[cfg(feature = "uuid")]
30pub struct MakeRequestUuid;
31
32#[cfg(feature = "uuid")]
33impl MakeRequestId for MakeRequestUuid {
34    fn make_request_id<B>(
35        &mut self,
36        #[allow(unused_variables)] request: &Request<B>,
37    ) -> Option<RequestId> {
38        Some(RequestId::new(
39            Uuid::new_v4()
40                .to_string()
41                .parse()
42                .expect("uuid should only contain ascii characters"),
43        ))
44    }
45}
46
47/// A [`MakeRequestId`] that generates a [`RequestId`] from a [`Ulid`].
48///
49/// [`MakeRequestId`]: ::tower_http::request_id::MakeRequestId
50/// [`RequestId`]: ::tower_http::request_id::RequestId
51/// [`Ulid`]: ::ulid::Ulid
52#[derive(Clone, Copy, Debug, Default)]
53#[cfg(feature = "ulid")]
54pub struct MakeRequestUlid;
55
56#[cfg(feature = "ulid")]
57impl MakeRequestId for MakeRequestUlid {
58    fn make_request_id<B>(
59        &mut self,
60        #[allow(unused_variables)] request: &Request<B>,
61    ) -> Option<RequestId> {
62        Some(RequestId::new(
63            Ulid::new()
64                .to_string()
65                .parse()
66                .expect("ulid should only contain ascii characters"),
67        ))
68    }
69}
70
71/// A [`MakeRequestId`] that uses an atomic counter to generate [`RequestId`]s.
72///
73/// [`MakeRequestId`]: ::tower_http::request_id::MakeRequestId
74/// [`RequestId`]: ::tower_http::request_id::RequestId
75#[derive(Clone, Debug, Default)]
76pub struct MakeRequestIdCounter {
77    counter: Arc<AtomicUsize>,
78}
79
80impl MakeRequestIdCounter {
81    /// Create a new `MakeRequestIdCounter`.
82    #[must_use]
83    pub fn new() -> Self {
84        Self::default()
85    }
86}
87
88impl MakeRequestId for MakeRequestIdCounter {
89    fn make_request_id<B>(
90        &mut self,
91        #[allow(unused_variables)] request: &Request<B>,
92    ) -> Option<RequestId> {
93        Some(RequestId::new(
94            self.counter
95                .fetch_add(1, Ordering::Relaxed)
96                .to_string()
97                .parse()
98                .expect("usize should only contain ascii characters"),
99        ))
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use hyper::Request;
106    use tower_http::request_id::MakeRequestId;
107
108    use super::*;
109
110    #[test]
111    fn make_request_id_counter() {
112        let mut make_request_id = MakeRequestIdCounter::new();
113
114        let next = make_request_id
115            .make_request_id(&Request::new(()))
116            .unwrap()
117            .into_header_value();
118
119        assert_eq!("0", next.to_str().unwrap());
120
121        let next = make_request_id
122            .make_request_id(&Request::new(()))
123            .unwrap()
124            .into_header_value();
125
126        assert_eq!("1", next.to_str().unwrap());
127    }
128
129    #[test]
130    #[cfg(feature = "uuid")]
131    fn make_request_uuid() {
132        let mut make_request_id = MakeRequestUuid;
133
134        let next = make_request_id
135            .make_request_id(&Request::new(()))
136            .unwrap()
137            .into_header_value();
138
139        assert!(uuid::Uuid::try_parse_ascii(next.as_bytes()).is_ok());
140    }
141
142    #[test]
143    #[cfg(feature = "ulid")]
144    fn make_request_ulid() {
145        let mut make_request_id = MakeRequestUlid;
146
147        let next = make_request_id
148            .make_request_id(&Request::new(()))
149            .unwrap()
150            .into_header_value();
151
152        assert!(ulid::Ulid::from_string(next.to_str().unwrap()).is_ok());
153    }
154}