Skip to main content

tokn_headers/
map.rs

1//! Vec-backed, order- and case-preserving header map.
2//!
3//! Designed for typical HTTP request/response sizes (< 30 entries), where
4//! linear scan beats hash-table overhead. Supports duplicate names (required
5//! for headers like `Set-Cookie`) by storing all entries in insertion order.
6//!
7//! Lookup is case-insensitive via [`HeaderName`]'s `Eq` impl.
8
9use crate::name::HeaderName;
10use crate::value::HeaderValue;
11use serde::de::{self, MapAccess, SeqAccess, Visitor};
12use serde::ser::{SerializeMap, Serializer};
13use serde::{Deserialize, Deserializer, Serialize};
14use std::borrow::Cow;
15use std::fmt;
16
17/// Trait for values that can be used as a lookup key against a [`HeaderMap`].
18///
19/// Implementations exist for [`HeaderName`] and `&HeaderName` (zero-allocation,
20/// reusing the cached lowercase form), and for `&str` / `String` /
21/// `&String` (lowercased on the fly when needed).
22pub trait AsHeaderName {
23  /// Return the ASCII-lowercase form of this name. Borrows from the value
24  /// when already lowercase, allocates otherwise.
25  fn as_lower(&self) -> Cow<'_, str>;
26}
27
28impl AsHeaderName for HeaderName {
29  fn as_lower(&self) -> Cow<'_, str> {
30    Cow::Borrowed(self.as_str())
31  }
32}
33
34impl AsHeaderName for &HeaderName {
35  fn as_lower(&self) -> Cow<'_, str> {
36    Cow::Borrowed(self.as_str())
37  }
38}
39
40impl AsHeaderName for str {
41  fn as_lower(&self) -> Cow<'_, str> {
42    if self.bytes().all(|b| !b.is_ascii_uppercase()) {
43      Cow::Borrowed(self)
44    } else {
45      Cow::Owned(self.to_ascii_lowercase())
46    }
47  }
48}
49
50impl AsHeaderName for &str {
51  fn as_lower(&self) -> Cow<'_, str> {
52    <str as AsHeaderName>::as_lower(self)
53  }
54}
55
56impl AsHeaderName for String {
57  fn as_lower(&self) -> Cow<'_, str> {
58    <str as AsHeaderName>::as_lower(self.as_str())
59  }
60}
61
62impl AsHeaderName for &String {
63  fn as_lower(&self) -> Cow<'_, str> {
64    <str as AsHeaderName>::as_lower(self.as_str())
65  }
66}
67
68/// A list of (name, value) pairs in insertion order.
69#[derive(Debug, Clone, Default, PartialEq, Eq)]
70pub struct HeaderMap {
71  entries: Vec<(HeaderName, HeaderValue)>,
72}
73
74impl HeaderMap {
75  /// Create an empty map.
76  pub fn new() -> Self {
77    Self { entries: Vec::new() }
78  }
79
80  /// Create a map with capacity for `n` entries (no allocations until exceeded).
81  pub fn with_capacity(n: usize) -> Self {
82    Self {
83      entries: Vec::with_capacity(n),
84    }
85  }
86
87  /// Append `(name, value)` to the map. Allows duplicate names — use this
88  /// only for headers that are semantically multi-valued (`Set-Cookie`,
89  /// `Via`, etc.). For the common case of "set this header to this value",
90  /// use [`Self::insert`].
91  pub fn append(&mut self, name: impl Into<HeaderName>, value: impl Into<HeaderValue>) {
92    self.entries.push((name.into(), value.into()));
93  }
94
95  /// Set `name` to `value`, replacing any existing entries with the same
96  /// name. Matches `http::HeaderMap::insert` / `reqwest::HeaderMap::insert`
97  /// semantics: a single entry with this name will exist after the call.
98  ///
99  /// The new entry is placed at the position of the first existing match
100  /// (preserving header order when overwriting), otherwise appended.
101  pub fn insert(&mut self, name: impl Into<HeaderName>, value: impl Into<HeaderValue>) {
102    let name = name.into();
103    let value = value.into();
104    let mut first = None;
105    let mut i = 0;
106    while i < self.entries.len() {
107      if self.entries[i].0 == name {
108        if first.is_none() {
109          first = Some(i);
110          self.entries[i].1 = value.clone();
111          i += 1;
112        } else {
113          self.entries.remove(i);
114        }
115      } else {
116        i += 1;
117      }
118    }
119    if first.is_none() {
120      self.entries.push((name, value));
121    }
122  }
123
124  /// Remove all entries matching `name`. Returns the count removed.
125  pub fn remove<N: AsHeaderName>(&mut self, name: N) -> usize {
126    let lower = name.as_lower();
127    let before = self.entries.len();
128    self.entries.retain(|(n, _)| n.as_str() != lower.as_ref());
129    before - self.entries.len()
130  }
131
132  /// First value matching `name`, if any.
133  pub fn get<N: AsHeaderName>(&self, name: N) -> Option<&HeaderValue> {
134    let lower = name.as_lower();
135    self
136      .entries
137      .iter()
138      .find(|(n, _)| n.as_str() == lower.as_ref())
139      .map(|(_, v)| v)
140  }
141
142  /// All values matching `name`, in insertion order.
143  pub fn get_all<N: AsHeaderName>(&self, name: N) -> impl Iterator<Item = &HeaderValue> {
144    let lower = name.as_lower().into_owned();
145    self
146      .entries
147      .iter()
148      .filter(move |(n, _)| n.as_str() == lower.as_str())
149      .map(|(_, v)| v)
150  }
151
152  /// Whether at least one entry matches `name`.
153  pub fn contains_key<N: AsHeaderName>(&self, name: N) -> bool {
154    let lower = name.as_lower();
155    self.entries.iter().any(|(n, _)| n.as_str() == lower.as_ref())
156  }
157
158  /// Iterate entries in insertion order.
159  pub fn iter(&self) -> impl Iterator<Item = (&HeaderName, &HeaderValue)> {
160    self.entries.iter().map(|(n, v)| (n, v))
161  }
162
163  /// Number of entries (counts duplicates).
164  pub fn len(&self) -> usize {
165    self.entries.len()
166  }
167
168  /// Whether the map is empty.
169  pub fn is_empty(&self) -> bool {
170    self.entries.is_empty()
171  }
172
173  /// Append all entries from `other` in their original order. Duplicates are
174  /// preserved (this does not deduplicate against `self`).
175  pub fn extend(&mut self, other: HeaderMap) {
176    self.entries.extend(other.entries);
177  }
178
179  /// Append all entries from `other`, with later names winning over earlier ones
180  /// in `self`. Within `other`, all entries are kept (even duplicate names).
181  pub fn merge_replacing(&mut self, other: HeaderMap) {
182    for (name, value) in &other.entries {
183      self.entries.retain(|(n, _)| n != name);
184      let _ = value;
185    }
186    self.entries.extend(other.entries);
187  }
188}
189
190impl FromIterator<(HeaderName, HeaderValue)> for HeaderMap {
191  fn from_iter<I: IntoIterator<Item = (HeaderName, HeaderValue)>>(iter: I) -> Self {
192    Self {
193      entries: iter.into_iter().collect(),
194    }
195  }
196}
197
198impl IntoIterator for HeaderMap {
199  type Item = (HeaderName, HeaderValue);
200  type IntoIter = std::vec::IntoIter<Self::Item>;
201  fn into_iter(self) -> Self::IntoIter {
202    self.entries.into_iter()
203  }
204}
205
206impl<'a> IntoIterator for &'a HeaderMap {
207  type Item = (&'a HeaderName, &'a HeaderValue);
208  type IntoIter = std::iter::Map<
209    std::slice::Iter<'a, (HeaderName, HeaderValue)>,
210    fn(&'a (HeaderName, HeaderValue)) -> (&'a HeaderName, &'a HeaderValue),
211  >;
212  fn into_iter(self) -> Self::IntoIter {
213    self.entries.iter().map(|(n, v)| (n, v))
214  }
215}
216
217// --- serde ---
218//
219// JSON shape: object keyed by the **original-cased** header name, with the
220// value either a single string (one entry for that name) or an array of strings
221// (multiple entries, in insertion order).
222//
223// Deserialization accepts both shapes (string-or-array-of-strings) and is
224// also backward-compatible with legacy DB rows whose values are always single
225// strings (as written by the previous `reqwest::header::HeaderMap` serde impl).
226
227impl Serialize for HeaderMap {
228  fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
229    // Group consecutive duplicate names, preserving first-occurrence order.
230    let mut grouped: Vec<(&HeaderName, Vec<&HeaderValue>)> = Vec::with_capacity(self.entries.len());
231    'outer: for (n, v) in &self.entries {
232      for (existing_name, vs) in grouped.iter_mut() {
233        if *existing_name == n {
234          vs.push(v);
235          continue 'outer;
236        }
237      }
238      grouped.push((n, vec![v]));
239    }
240    let mut map = serializer.serialize_map(Some(grouped.len()))?;
241    for (name, vals) in grouped {
242      if vals.len() == 1 {
243        map.serialize_entry(name, vals[0])?;
244      } else {
245        map.serialize_entry(name, &vals)?;
246      }
247    }
248    map.end()
249  }
250}
251
252/// Visitor accepting either a single string or a sequence of strings as the
253/// value side of a header-map entry.
254struct StringOrSeq;
255
256impl<'de> Visitor<'de> for StringOrSeq {
257  type Value = Vec<String>;
258
259  fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
260    f.write_str("a string or array of strings")
261  }
262
263  fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
264    Ok(vec![v.to_string()])
265  }
266
267  fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E> {
268    Ok(vec![v])
269  }
270
271  fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
272    let mut out = Vec::with_capacity(seq.size_hint().unwrap_or(0));
273    while let Some(s) = seq.next_element::<String>()? {
274      out.push(s);
275    }
276    Ok(out)
277  }
278}
279
280#[derive(Default)]
281struct HeaderMapVisitor;
282
283impl<'de> Visitor<'de> for HeaderMapVisitor {
284  type Value = HeaderMap;
285
286  fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
287    f.write_str("a map of header name to string or array of strings")
288  }
289
290  fn visit_map<A: MapAccess<'de>>(self, mut access: A) -> Result<Self::Value, A::Error> {
291    let mut out = HeaderMap::with_capacity(access.size_hint().unwrap_or(0));
292    while let Some(name) = access.next_key::<HeaderName>()? {
293      let values = access.next_value_seed(StringOrSeqSeed)?;
294      for v in values {
295        out.append(name.clone(), HeaderValue::from_string(v));
296      }
297    }
298    Ok(out)
299  }
300}
301
302struct StringOrSeqSeed;
303
304impl<'de> de::DeserializeSeed<'de> for StringOrSeqSeed {
305  type Value = Vec<String>;
306  fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
307    deserializer.deserialize_any(StringOrSeq)
308  }
309}
310
311impl<'de> Deserialize<'de> for HeaderMap {
312  fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
313    deserializer.deserialize_map(HeaderMapVisitor)
314  }
315}
316
317#[cfg(test)]
318mod tests {
319  use super::*;
320
321  fn name(s: &'static str) -> HeaderName {
322    HeaderName::new(s)
323  }
324
325  #[test]
326  fn insert_preserves_order_and_case() {
327    let mut m = HeaderMap::new();
328    m.insert(name("X-Aaa"), "1");
329    m.insert(name("Authorization"), "Bearer x");
330    m.insert(name("Content-Type"), "application/json");
331    let collected: Vec<_> = m
332      .iter()
333      .map(|(n, v)| (n.original().to_string(), v.as_str().to_string()))
334      .collect();
335    assert_eq!(
336      collected,
337      vec![
338        ("X-Aaa".into(), "1".into()),
339        ("Authorization".into(), "Bearer x".into()),
340        ("Content-Type".into(), "application/json".into()),
341      ]
342    );
343  }
344
345  #[test]
346  fn get_is_case_insensitive() {
347    let mut m = HeaderMap::new();
348    m.insert(name("Authorization"), "Bearer x");
349    assert_eq!(m.get(name("authorization")).map(|v| v.as_str()), Some("Bearer x"));
350    assert_eq!(m.get(name("AUTHORIZATION")).map(|v| v.as_str()), Some("Bearer x"));
351  }
352
353  #[test]
354  fn duplicates_preserved() {
355    let mut m = HeaderMap::new();
356    m.append(name("Set-Cookie"), "a=1");
357    m.append(name("Set-Cookie"), "b=2");
358    let all: Vec<_> = m.get_all(&name("set-cookie")).map(|v| v.as_str().to_string()).collect();
359    assert_eq!(all, vec!["a=1", "b=2"]);
360    assert_eq!(m.len(), 2);
361  }
362
363  #[test]
364  fn insert_overrides_existing_value() {
365    let mut m = HeaderMap::new();
366    m.insert(name("Authorization"), "Bearer old");
367    m.insert(name("authorization"), "Bearer new");
368    assert_eq!(m.len(), 1);
369    assert_eq!(m.get(name("authorization")).map(|v| v.as_str()), Some("Bearer new"));
370  }
371
372  #[test]
373  fn insert_removes_duplicates_keeps_position() {
374    let mut m = HeaderMap::new();
375    m.insert(name("A"), "1");
376    m.append(name("Set-Cookie"), "old1");
377    m.insert(name("B"), "2");
378    m.append(name("Set-Cookie"), "old2");
379    m.insert(name("set-cookie"), "new");
380    let collected: Vec<_> = m
381      .iter()
382      .map(|(n, v)| (n.original().to_string(), v.as_str().to_string()))
383      .collect();
384    assert_eq!(
385      collected,
386      vec![
387        ("A".into(), "1".into()),
388        ("Set-Cookie".into(), "new".into()),
389        ("B".into(), "2".into()),
390      ]
391    );
392  }
393
394  #[test]
395  fn remove_returns_count() {
396    let mut m = HeaderMap::new();
397    m.append(name("X"), "1");
398    m.append(name("X"), "2");
399    m.insert(name("Y"), "3");
400    assert_eq!(m.remove(name("x")), 2);
401    assert_eq!(m.len(), 1);
402  }
403
404  #[test]
405  fn extend_keeps_duplicates() {
406    let mut a = HeaderMap::new();
407    a.append(name("X"), "1");
408    let mut b = HeaderMap::new();
409    b.append(name("X"), "2");
410    a.extend(b);
411    assert_eq!(a.len(), 2);
412  }
413
414  #[test]
415  fn merge_replacing_overrides_existing() {
416    let mut a = HeaderMap::new();
417    a.insert(name("X"), "old");
418    a.insert(name("Y"), "keep");
419    let mut b = HeaderMap::new();
420    b.insert(name("x"), "new");
421    a.merge_replacing(b);
422    let collected: Vec<_> = a
423      .iter()
424      .map(|(n, v)| (n.original().to_string(), v.as_str().to_string()))
425      .collect();
426    assert_eq!(collected, vec![("Y".into(), "keep".into()), ("x".into(), "new".into())]);
427  }
428
429  #[test]
430  fn ref_into_iter_yields_borrowed_pairs() {
431    let mut m = HeaderMap::new();
432    m.insert(name("A"), "1");
433    m.insert(name("B"), "2");
434    let collected: Vec<_> = (&m).into_iter().map(|(n, v)| (n.as_str(), v.as_str())).collect();
435    assert_eq!(collected, vec![("a", "1"), ("b", "2")]);
436  }
437
438  #[test]
439  fn serde_round_trip_preserves_case_and_duplicates() {
440    let mut m = HeaderMap::new();
441    m.insert(name("Authorization"), "Bearer x");
442    m.append(name("Set-Cookie"), "a=1");
443    m.append(name("Set-Cookie"), "b=2");
444    m.insert(name("Content-Type"), "application/json");
445    let json = serde_json::to_string(&m).unwrap();
446    assert!(json.contains("\"Authorization\":\"Bearer x\""), "got {json}");
447    assert!(json.contains("\"Set-Cookie\":[\"a=1\",\"b=2\"]"), "got {json}");
448    let back: HeaderMap = serde_json::from_str(&json).unwrap();
449    assert_eq!(back.len(), 4);
450    assert_eq!(back.get(name("authorization")).unwrap().as_str(), "Bearer x");
451    let cookie_name = name("set-cookie");
452    let cookies: Vec<_> = back.get_all(&cookie_name).map(|v| v.as_str()).collect();
453    assert_eq!(cookies, vec!["a=1", "b=2"]);
454  }
455
456  #[test]
457  fn serde_deserialize_accepts_legacy_string_only_object() {
458    // Legacy DB shape: every value a single string, no arrays.
459    let json = r#"{"authorization":"Bearer x","content-type":"application/json"}"#;
460    let m: HeaderMap = serde_json::from_str(json).unwrap();
461    assert_eq!(m.len(), 2);
462    assert_eq!(m.get(name("Authorization")).unwrap().as_str(), "Bearer x");
463  }
464
465  #[test]
466  fn serde_deserialize_accepts_array_value() {
467    let json = r#"{"X-Foo":["a","b","c"]}"#;
468    let m: HeaderMap = serde_json::from_str(json).unwrap();
469    assert_eq!(m.len(), 3);
470    let foo = name("x-foo");
471    let vals: Vec<_> = m.get_all(&foo).map(|v| v.as_str()).collect();
472    assert_eq!(vals, vec!["a", "b", "c"]);
473  }
474}