1use http::{HeaderMap, HeaderName, HeaderValue};
2
3use crate::VERSION;
4
5const PROTECTED: &[&str] = &[
6 "authorization",
7 "accept",
8 "user-agent",
9 "x-typesafe-sdk",
10 "x-typesafe-runtime",
11 "x-typesafe-retry-count",
12];
13
14pub(crate) fn is_protected(name: &HeaderName) -> bool {
15 PROTECTED.contains(&name.as_str())
16}
17
18pub(crate) fn merge_user_headers(dst: &mut HeaderMap, src: &HeaderMap) {
20 for (name, value) in src {
21 if !is_protected(name) {
22 dst.insert(name.clone(), value.clone());
23 }
24 }
25}
26
27pub(crate) fn user_agent() -> HeaderValue {
28 static_or_owned(&format!("typesafe-rs/{VERSION}"))
29}
30
31pub(crate) fn sdk_header() -> HeaderValue {
32 static_or_owned(&format!("typesafe-rs/{VERSION}"))
33}
34
35pub(crate) fn runtime_header() -> HeaderValue {
36 static_or_owned(&format!(
37 "rust/{}; {}-{}",
38 env!("TYPESAFE_RUSTC_VERSION"),
39 env!("TYPESAFE_TARGET_OS"),
40 env!("TYPESAFE_TARGET_ARCH"),
41 ))
42}
43
44fn static_or_owned(value: &str) -> HeaderValue {
45 HeaderValue::from_str(value).unwrap_or_else(|_| HeaderValue::from_static("typesafe-rs"))
46}
47
48pub(crate) fn request_id(headers: &HeaderMap) -> Option<String> {
49 headers
50 .get("x-typesafe-request-id")
51 .and_then(|v| v.to_str().ok())
52 .map(str::to_owned)
53}
54
55pub(crate) fn retry_count_value(attempt: u32) -> HeaderValue {
56 HeaderValue::from_str(&attempt.to_string()).unwrap_or_else(|_| HeaderValue::from_static("1"))
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use http::header::{ACCEPT, AUTHORIZATION, USER_AGENT};
63
64 #[test]
65 fn protected_headers_are_skipped() {
66 let mut dst = HeaderMap::new();
67 let mut src = HeaderMap::new();
68 src.insert(AUTHORIZATION, HeaderValue::from_static("Bearer stolen"));
69 src.insert(ACCEPT, HeaderValue::from_static("text/plain"));
70 src.insert(USER_AGENT, HeaderValue::from_static("other"));
71 src.insert("x-typesafe-sdk", HeaderValue::from_static("nope"));
72 src.insert("x-custom", HeaderValue::from_static("ok"));
73 merge_user_headers(&mut dst, &src);
74 assert!(dst.get(AUTHORIZATION).is_none());
75 assert!(dst.get(ACCEPT).is_none());
76 assert_eq!(dst.get("x-custom").unwrap(), "ok");
77 }
78}