1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
use log::warn;
use http::header::HeaderMap;
use rswappalyzer_engine::input_evidence::header_evidence::StandardCookie;
use rustc_hash::FxHashMap;
/// Header转换工具结构体
/// 设计:无状态工具类,所有方法为关联函数(static)
pub struct HeaderConverter;
impl HeaderConverter {
/// 将标准HeaderMap转换为FxHashMap<String, Vec<String>>
/// 特性:
/// 1. 迭代次数限制(最多1000次),防止恶意超大Header
/// 2. 所有Key/Value转为小写,统一匹配规则
/// 3. FxHashMap高性能哈希表,适合高频访问
/// 参数:header_map - 标准HTTP HeaderMap
/// 返回:转换后的多值Header哈希表
pub fn to_hashmap(header_map: &HeaderMap) -> FxHashMap<String, Vec<String>> {
let mut map = FxHashMap::default();
let mut iter_count = 0;
for (key, value) in header_map.iter() {
iter_count += 1;
// 安全防护:限制迭代次数,防止恶意构造的超大Header
if iter_count > 1000 {
warn!("Header iteration exceeded 1000 times, forced termination");
break;
}
// 统一转为小写,避免大小写敏感问题
let key_str = key.as_str().to_ascii_lowercase();
let value_str = value.to_str().unwrap_or("").to_ascii_lowercase();
// 按Key聚合多值Header
map.entry(key_str).or_insert_with(Vec::new).push(value_str);
}
map
}
/// 将多值Header哈希表转换为单值Header哈希表
/// 规则:取每个Key的第一个非空值
/// 参数:hashmap - 多值Header哈希表
/// 返回:单值Header哈希表(每个Key仅保留第一个非空值)
pub fn to_single_value(hashmap: &FxHashMap<String, Vec<String>>) -> FxHashMap<String, String> {
let mut single_map = FxHashMap::default();
for (key, values) in hashmap {
// 查找第一个非空值,忽略空值
if let Some(first_val) = values.iter().find(|v| !v.is_empty()) {
single_map.insert(key.clone(), first_val.clone());
}
}
single_map
}
/// 批量转换Header(单值Header + Cookie专用Header)
/// 特性:
/// 1. 预分配哈希表容量,避免运行期扩容开销
/// 2. 分离普通Header(单值)和Cookie Header(多值)
/// 3. 迭代次数限制,防止恶意超大Header
/// 参数:headers - 标准HTTP HeaderMap
/// 返回:(单值普通Header哈希表, Cookie专用Header哈希表)
pub fn convert_all(
headers: &HeaderMap,
) -> (FxHashMap<String, String>, FxHashMap<String, Vec<String>>) {
let mut single_header_map = FxHashMap::default();
let mut cookie_map: FxHashMap<String, Vec<String>> = FxHashMap::default();
let mut iter_count = 0;
// 预分配容量:避免运行期扩容,提升性能
single_header_map.reserve(headers.len());
cookie_map.reserve(2); // Cookie相关Header最多2个(cookie/set-cookie)
for (k, v) in headers.iter() {
iter_count += 1;
// 安全防护:限制迭代次数
if iter_count > 1000 {
warn!("Header iteration exceeded 1000 times, forced termination");
break;
}
// 统一转为小写ASCII,避免大小写问题
let key = k.as_str().to_ascii_lowercase();
// 安全转换Header值为字符串,失败则返回空字符串
let value = match v.to_str() {
Ok(s) => s.to_ascii_lowercase(),
Err(_) => String::new(),
};
// 分离Cookie相关Header和普通Header
if key == "cookie" || key == "set-cookie" {
cookie_map.entry(key).or_default().push(value);
} else {
single_header_map.insert(key, value);
}
}
(single_header_map, cookie_map)
}
/// 解析原始Cookie Header为标准化Cookie实体列表
/// 输入:原始Cookie Header哈希表 { "set-cookie": [...], "cookie": [...] }
/// 输出:标准化Cookie实体列表
/// 特性:
/// 1. 过滤deleted Cookie,避免无效匹配
/// 2. 统一Cookie名小写,避免大小写敏感
/// 3. 高性能解析,手写循环替代迭代器
pub fn parse_to_standard_cookie(
raw_cookie_header_map: &FxHashMap<String, Vec<String>>
) -> Vec<StandardCookie> {
let mut standard_cookies = Vec::new();
// 分别解析Set-Cookie和Request-Cookie
for (header_name, raw_cookie_values) in raw_cookie_header_map {
let source = header_name.as_str(); // 来源Header名称("cookie"/"set-cookie")
match source {
"set-cookie" => {
for raw in raw_cookie_values {
// 解析Set-Cookie并添加到列表
if let Some(cookie) = Self::parse_set_cookie_fast(raw, source) {
standard_cookies.push(cookie);
}
}
}
"cookie" => {
for raw in raw_cookie_values {
// 解析Request-Cookie(返回多个Cookie)
let cookies = Self::parse_request_cookie_fast(raw, source);
standard_cookies.extend(cookies);
}
}
_ => continue,
}
}
standard_cookies
}
/// 快速解析Set-Cookie头为标准化Cookie实体
/// 返回:Option<StandardCookie>(过滤无效Cookie)
fn parse_set_cookie_fast(raw_cookie: &str, source: &str) -> Option<StandardCookie> {
let cookie_str = raw_cookie.trim();
if cookie_str.is_empty() { return None; }
// 分割Cookie核心KV和属性(仅处理第一个分号前的内容)
let mut segments = cookie_str.split(';').map(|s| s.trim()).filter(|s| !s.is_empty());
let Some(core_kv) = segments.next() else { return None; };
// 查找等号位置,分割Key和Value
let eq_pos = core_kv.find('=')?;
let (name, value) = (core_kv[0..eq_pos].trim(), core_kv[eq_pos+1..].trim());
// 过滤规则:
// 1. Cookie名不能为空
// 2. Value不能是"deleted"(忽略已删除的Cookie)
if name.is_empty() || value.eq_ignore_ascii_case("deleted") {
return None;
}
// 统一Cookie名小写,构建实体
Some(StandardCookie {
name: name.to_ascii_lowercase(),
value: value.to_string(),
source: source.to_string(),
})
}
/// 快速解析Request-Cookie头为标准化Cookie实体列表
/// 返回:Vec<StandardCookie>(多个Cookie实体)
fn parse_request_cookie_fast(raw_cookie: &str, source: &str) -> Vec<StandardCookie> {
let mut cookies = Vec::new();
let cookie_str = raw_cookie.trim();
if cookie_str.is_empty() { return cookies; }
// 手写split+trim+filter,替代链式迭代器,提升性能
let mut start = 0;
let bytes = cookie_str.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
if b == b';' {
let slice = &bytes[start..i];
let core_kv = Self::trim_slice(slice);
if !core_kv.is_empty() {
if let Some(cookie) = Self::parse_cookie_kv(core_kv, source) {
cookies.push(cookie);
}
}
start = i + 1;
}
}
// 处理最后一个KV对
let slice = &bytes[start..];
let core_kv = Self::trim_slice(slice);
if !core_kv.is_empty() {
if let Some(cookie) = Self::parse_cookie_kv(core_kv, source) {
cookies.push(cookie);
}
}
cookies
}
/// 辅助函数:字节切片trim(零拷贝,比str.trim()更快)
/// 特性:
/// 1. 零拷贝:仅操作字节切片,无内存分配
/// 2. ASCII空白符处理,适合HTTP Header场景
/// 3. 内联优化,编译期嵌入调用处
/// 参数:slice - 原始字节切片
/// 返回:trim后的字节切片
#[inline(always)]
fn trim_slice(slice: &[u8]) -> &[u8] {
// 查找第一个非空白字符的位置
let start = slice.iter().position(|&b| !b.is_ascii_whitespace()).unwrap_or(0);
// 查找最后一个非空白字符的位置
let end = slice.iter().rposition(|&b| !b.is_ascii_whitespace()).map_or(0, |i| i + 1);
&slice[start..end]
}
/// 辅助函数:解析Cookie KV对为实体
#[inline(always)]
fn parse_cookie_kv(core_kv: &[u8], source: &str) -> Option<StandardCookie> {
// 查找等号位置
let eq_pos = core_kv.iter().position(|&b| b == b'=')?;
let (name_slice, value_slice) = core_kv.split_at(eq_pos);
// Trim名称和值
let name = Self::trim_slice(name_slice);
let value = if value_slice.is_empty() {
&[]
} else {
Self::trim_slice(&value_slice[1..])
};
// 过滤空名称
if name.is_empty() { return None; }
// UTF8安全转换,统一转为小写
let name_str = String::from_utf8_lossy(name).to_ascii_lowercase();
let value_str = String::from_utf8_lossy(value).to_string();
// 过滤deleted值
if value_str.eq_ignore_ascii_case("deleted") { return None; }
// 构建标准化Cookie实体
Some(StandardCookie {
name: name_str,
value: value_str,
source: source.to_string(),
})
}
}