qubit_http/options/sensitive_http_headers.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9
10use std::collections::BTreeSet;
11
12use crate::constants::DEFAULT_SENSITIVE_HEADER_NAMES;
13
14/// Case-insensitive set of HTTP header names whose values should be masked in logs.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct SensitiveHttpHeaders {
17 /// Normalized lowercase header names.
18 headers: BTreeSet<String>,
19}
20
21impl SensitiveHttpHeaders {
22 /// Creates an empty set (no names marked sensitive).
23 ///
24 /// # Returns
25 /// New [`SensitiveHttpHeaders`] without default names; prefer
26 /// [`SensitiveHttpHeaders::default`] for built-ins.
27 pub fn new() -> Self {
28 Self {
29 headers: BTreeSet::new(),
30 }
31 }
32
33 /// Returns whether `header_name` is treated as sensitive (compared case-insensitively).
34 ///
35 /// # Parameters
36 /// - `header_name`: Header name to test (any casing).
37 ///
38 /// # Returns
39 /// `true` if masked in logging helpers.
40 pub fn contains(&self, header_name: &str) -> bool {
41 self.headers.contains(&header_name.to_lowercase())
42 }
43
44 /// Inserts one header name after trimming and lowercasing; ignores empty strings.
45 ///
46 /// # Parameters
47 /// - `header_name`: Name to mark sensitive.
48 pub fn insert(&mut self, header_name: &str) {
49 let value = header_name.trim().to_lowercase();
50 if !value.is_empty() {
51 self.headers.insert(value);
52 }
53 }
54
55 /// Inserts each header from the iterator via [`SensitiveHttpHeaders::insert`].
56 ///
57 /// # Parameters
58 /// - `headers`: Iterator of header name-like values.
59 pub fn extend<I, S>(&mut self, headers: I)
60 where
61 I: IntoIterator<Item = S>,
62 S: AsRef<str>,
63 {
64 for header in headers {
65 self.insert(header.as_ref());
66 }
67 }
68
69 /// Clears all stored sensitive header names.
70 pub fn clear(&mut self) {
71 self.headers.clear();
72 }
73
74 /// Returns how many sensitive header names are stored.
75 ///
76 /// # Returns
77 /// Count of entries in the internal set.
78 pub fn len(&self) -> usize {
79 self.headers.len()
80 }
81
82 /// Returns whether no sensitive names are registered.
83 ///
84 /// # Returns
85 /// `true` if [`SensitiveHttpHeaders::len`] is zero.
86 pub fn is_empty(&self) -> bool {
87 self.headers.is_empty()
88 }
89
90 /// Iterates normalized (lowercase) sensitive header names.
91 ///
92 /// # Returns
93 /// Iterator over string slices owned by `self`.
94 pub fn iter(&self) -> impl Iterator<Item = &str> {
95 self.headers.iter().map(String::as_str)
96 }
97}
98
99impl Default for SensitiveHttpHeaders {
100 /// Starts with [`crate::DEFAULT_SENSITIVE_HEADER_NAMES`] pre-registered.
101 ///
102 /// # Returns
103 /// Non-empty [`SensitiveHttpHeaders`].
104 fn default() -> Self {
105 let mut result = SensitiveHttpHeaders::new();
106 result.extend(DEFAULT_SENSITIVE_HEADER_NAMES);
107 result
108 }
109}