Skip to main content

tokn_headers/
reqwest_compat.rs

1//! Conversions between [`crate::HeaderMap`] and [`reqwest::header::HeaderMap`]
2//! used at the wire boundary.
3//!
4//! `reqwest::header::HeaderName` lowercases its input internally, so the
5//! original casing of names is **not** preserved when crossing the boundary
6//! in either direction. Order is preserved going `crate::HeaderMap ->
7//! reqwest::header::HeaderMap` (we use `append`); the reverse direction
8//! preserves whatever order `reqwest`'s iterator yields (insertion order in
9//! practice).
10
11use crate::map::HeaderMap;
12use crate::value::HeaderValue;
13use reqwest::header::{HeaderMap as ReqMap, HeaderName as ReqName, HeaderValue as ReqValue};
14
15impl From<HeaderMap> for ReqMap {
16  fn from(value: HeaderMap) -> Self {
17    let mut out = ReqMap::with_capacity(value.len());
18    for (name, val) in value {
19      let Ok(rn) = ReqName::from_bytes(name.as_str().as_bytes()) else {
20        continue;
21      };
22      let Ok(rv) = ReqValue::from_str(val.as_str()) else {
23        continue;
24      };
25      out.append(rn, rv);
26    }
27    out
28  }
29}
30
31impl From<&ReqMap> for HeaderMap {
32  fn from(value: &ReqMap) -> Self {
33    let mut out = HeaderMap::with_capacity(value.len());
34    for (name, val) in value {
35      let Ok(s) = val.to_str() else { continue };
36      out.insert(name.as_str(), HeaderValue::from_string(s.to_string()));
37    }
38    out
39  }
40}
41
42#[cfg(test)]
43mod tests {
44  use super::*;
45  #[test]
46  fn round_trip_preserves_values_and_count() {
47    let mut m = HeaderMap::new();
48    m.insert("Authorization", "Bearer abc");
49    m.insert("Content-Type", "application/json");
50    m.insert("X-Session-Id", "ses_42");
51    let r: ReqMap = m.clone().into();
52    assert_eq!(r.len(), 3);
53    assert_eq!(r.get("authorization").unwrap().to_str().unwrap(), "Bearer abc");
54    let back: HeaderMap = (&r).into();
55    assert_eq!(back.len(), 3);
56    assert_eq!(back.get("authorization").unwrap().as_str(), "Bearer abc");
57  }
58
59  #[test]
60  fn duplicates_round_trip() {
61    let mut m = HeaderMap::new();
62    m.append("Set-Cookie", "a=1");
63    m.append("Set-Cookie", "b=2");
64    let r: ReqMap = m.into();
65    let all: Vec<_> = r.get_all("set-cookie").iter().map(|v| v.to_str().unwrap()).collect();
66    assert_eq!(all, vec!["a=1", "b=2"]);
67  }
68}