gotham/state/
request_id.rs1use hyper::header::HeaderMap;
4use log::trace;
5use uuid::Uuid;
6
7use crate::state::{FromState, State};
8
9pub(super) struct RequestId {
11 val: String,
12}
13
14pub(crate) fn set_request_id<'a>(state: &'a mut State) -> &'a str {
24 if !state.has::<RequestId>() {
25 let request_id = match HeaderMap::borrow_from(state).get("X-Request-ID") {
26 Some(ex_req_id) => {
27 let id = String::from_utf8(ex_req_id.as_bytes().into()).unwrap();
28 trace!(
29 "[{}] RequestId set from external source via X-Request-ID header",
30 id
31 );
32 RequestId { val: id }
33 }
34 None => {
35 let val = Uuid::new_v4().hyphenated().to_string();
36 trace!("[{}] RequestId generated internally", val);
37 RequestId { val }
38 }
39 };
40 state.put(request_id);
41 };
42
43 request_id(state)
44}
45
46pub fn request_id(state: &State) -> &str {
55 match RequestId::try_borrow_from(state) {
56 Some(request_id) => &request_id.val,
57 None => panic!("RequestId must be populated before application code is invoked"),
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 #[should_panic(expected = "RequestId must be populated before application code is invoked")]
67 fn panics_before_request_id_set() {
68 let state = State::new();
69 request_id(&state);
70 }
71
72 #[test]
73 fn uses_an_external_request_id() {
74 let mut state = State::new();
75
76 let mut headers = HeaderMap::new();
77 headers.insert("X-Request-ID", "1-2-3-4".to_owned().parse().unwrap());
78 state.put(headers);
79
80 {
81 let r = set_request_id(&mut state);
82 assert_eq!("1-2-3-4", r);
83 };
84 assert_eq!("1-2-3-4", request_id(&state));
85 }
86
87 #[test]
88 fn sets_a_unique_request_id() {
89 let mut state = State::new();
90 state.put(HeaderMap::new());
91
92 {
93 let r = set_request_id(&mut state);
94 assert_eq!(4, Uuid::parse_str(r).unwrap().get_version_num());
95 };
96 assert_eq!(
97 4,
98 Uuid::parse_str(request_id(&state))
99 .unwrap()
100 .get_version_num()
101 );
102 }
103
104 #[test]
105 fn does_not_overwrite_existant_request_id() {
106 let mut state = State::new();
107 state.put(RequestId {
108 val: "1-2-3-4".to_string(),
109 });
110
111 {
112 set_request_id(&mut state);
113 }
114 assert_eq!("1-2-3-4", request_id(&state));
115 }
116}