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