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