qubit_http/sanitize/sensitive_http_headers.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10
11use std::collections::BTreeSet;
12
13use super::default_sensitive_names::DEFAULT_SENSITIVE_HEADER_NAMES;
14
15/// Case-insensitive set of HTTP header names whose values should be masked in logs.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct SensitiveHttpHeaders {
18 /// Normalized lowercase header names.
19 headers: BTreeSet<String>,
20}
21
22impl SensitiveHttpHeaders {
23 /// Creates an empty set without built-in names.
24 ///
25 /// # Returns
26 /// Empty [`SensitiveHttpHeaders`].
27 pub fn new() -> Self {
28 Self {
29 headers: BTreeSet::new(),
30 }
31 }
32
33 /// Returns whether `header_name` is treated as sensitive.
34 ///
35 /// # Parameters
36 /// - `header_name`: Header name to test.
37 ///
38 /// # Returns
39 /// `true` if values for this header should be masked.
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.
45 ///
46 /// # Parameters
47 /// - `header_name`: Header 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 `headers`.
56 ///
57 /// # Parameters
58 /// - `headers`: Header names to mark sensitive.
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 sensitive header names.
70 pub fn clear(&mut self) {
71 self.headers.clear();
72 }
73
74 /// Returns the number of stored header names.
75 ///
76 /// # Returns
77 /// Stored header count.
78 pub fn len(&self) -> usize {
79 self.headers.len()
80 }
81
82 /// Returns whether no header names are stored.
83 ///
84 /// # Returns
85 /// `true` when empty.
86 pub fn is_empty(&self) -> bool {
87 self.headers.is_empty()
88 }
89
90 /// Iterates normalized header names.
91 ///
92 /// # Returns
93 /// Iterator over lowercase header names.
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 /// Creates a set containing built-in sensitive header names.
101 fn default() -> Self {
102 let mut result = SensitiveHttpHeaders::new();
103 result.extend(DEFAULT_SENSITIVE_HEADER_NAMES);
104 result
105 }
106}