use std::collections::HashMap;
use super::signatures::VendorSignature;
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct HeaderMatch {
pub(crate) header: String,
pub(crate) detail: String,
}
pub(crate) fn match_headers(
headers: &HashMap<String, String>,
signature: &VendorSignature,
) -> Option<HeaderMatch> {
for header in &signature.headers {
let Some(value) = headers.get(&header.name) else {
continue;
};
if value.is_empty() {
continue;
}
let matched = HeaderMatch {
header: header.name.clone(),
detail: format!("{} ({} header)", signature.label, header.name),
};
let Some(contains) = &header.contains else {
return Some(matched);
};
if value.to_lowercase().contains(contains) {
return Some(matched);
}
}
None
}
#[cfg(test)]
mod tests {
use super::super::signatures::signatures;
use super::*;
const FIXTURES: &str = include_str!("../../fixtures/antibot-headers.json");
#[test]
fn every_fixture_case_reproduces() {
let cases: serde_json::Value = serde_json::from_str(FIXTURES).expect("fixture JSON");
for case in cases.as_array().expect("array of cases") {
let name = case["name"].as_str().expect("name");
let headers: HashMap<String, String> =
serde_json::from_value(case["headers"].clone()).expect("headers map");
for signature in signatures() {
let expected = case["expected"][&signature.key].as_str();
let actual = match_headers(&headers, signature);
assert_eq!(
actual.map(|m| m.detail).as_deref(),
expected,
"case {name:?}, vendor {}",
signature.key
);
}
}
}
}