rs_zero/rpc/
interceptor.rs1use tonic::{Request, Status, service::Interceptor};
2use uuid::Uuid;
3
4pub const REQUEST_ID_METADATA: &str = "x-request-id";
6
7pub fn request_id_interceptor() -> impl Interceptor {
9 |mut request: Request<()>| -> Result<Request<()>, Status> {
10 if !request.metadata().contains_key(REQUEST_ID_METADATA) {
11 let request_id = Uuid::new_v4().to_string();
12 let value = request_id
13 .parse()
14 .map_err(|_| Status::internal("invalid request id metadata"))?;
15 request.metadata_mut().insert(REQUEST_ID_METADATA, value);
16 }
17
18 Ok(request)
19 }
20}
21
22#[cfg(feature = "observability")]
24pub fn trace_context_interceptor() -> impl Interceptor {
25 |mut request: Request<()>| -> Result<Request<()>, Status> {
26 if !request
27 .metadata()
28 .contains_key(crate::observability::TRACEPARENT_HEADER)
29 {
30 #[cfg(feature = "otlp")]
31 {
32 crate::observability::inject_current_context_metadata(request.metadata_mut())
33 .map_err(|_| Status::internal("invalid traceparent metadata"))?;
34 }
35
36 #[cfg(not(feature = "otlp"))]
37 if let Some(traceparent) = crate::observability::current_traceparent() {
38 crate::observability::insert_traceparent_metadata(
39 request.metadata_mut(),
40 &traceparent,
41 )
42 .map_err(|_| Status::internal("invalid traceparent metadata"))?;
43 }
44 }
45
46 Ok(request)
47 }
48}
49
50pub fn deadline_interceptor(timeout: std::time::Duration) -> impl Interceptor {
52 move |mut request: Request<()>| -> Result<Request<()>, Status> {
53 if !request.metadata().contains_key("grpc-timeout") {
54 crate::rpc::deadline::insert_grpc_timeout(&mut request, timeout)
55 .map_err(|_| Status::internal("invalid grpc-timeout metadata"))?;
56 }
57 Ok(request)
58 }
59}
60
61pub fn rpc_resilience_key(service: &str, method: &str) -> String {
63 format!("{service}:{method}")
64}
65
66pub fn resilience_rejection_status(reason: impl std::fmt::Display) -> Status {
68 Status::unavailable(reason.to_string())
69}
70
71#[cfg(test)]
72mod tests {
73 use super::{
74 REQUEST_ID_METADATA, deadline_interceptor, request_id_interceptor,
75 resilience_rejection_status, rpc_resilience_key,
76 };
77 use tonic::{Request, service::Interceptor};
78
79 #[test]
80 fn interceptor_sets_request_id() {
81 let mut interceptor = request_id_interceptor();
82 let request = interceptor.call(Request::new(())).expect("request");
83
84 assert!(request.metadata().contains_key(REQUEST_ID_METADATA));
85 }
86
87 #[test]
88 fn interceptor_sets_grpc_timeout() {
89 let mut interceptor = deadline_interceptor(std::time::Duration::from_millis(30));
90 let request = interceptor.call(Request::new(())).expect("request");
91
92 assert!(request.metadata().contains_key("grpc-timeout"));
93 }
94
95 #[test]
96 fn rpc_resilience_helpers_are_stable() {
97 assert_eq!(rpc_resilience_key("hello", "Say"), "hello:Say");
98 assert_eq!(
99 resilience_rejection_status("open").code(),
100 tonic::Code::Unavailable
101 );
102 }
103}