Skip to main content

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}