use awp_types::RequesterType;
use axum::http::HeaderMap;
const AGENT_PATTERNS: &[&str] = &[
"bot",
"crawler",
"spider",
"agent",
"gpt",
"claude",
"gemini",
"perplexity",
"anthropic",
"openai",
];
pub fn detect_requester_type(headers: &HeaderMap) -> RequesterType {
if let Some(channel) = headers.get("X-AWP-Channel") {
if channel.to_str().unwrap_or("").eq_ignore_ascii_case("agent") {
return RequesterType::Agent;
}
}
let accept = headers.get("Accept").and_then(|v| v.to_str().ok()).unwrap_or("");
let ua = headers.get("User-Agent").and_then(|v| v.to_str().ok()).unwrap_or("");
let ua_lower = ua.to_lowercase();
let is_agent_accept = accept.contains("application/json")
|| accept.contains("application/ld+json")
|| accept.contains("application/awp+json");
let is_agent_ua = AGENT_PATTERNS.iter().any(|p| ua_lower.contains(p));
if is_agent_accept && is_agent_ua {
return RequesterType::Agent;
}
RequesterType::Human
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x_awp_channel_override() {
let mut headers = HeaderMap::new();
headers.insert("X-AWP-Channel", "agent".parse().unwrap());
headers.insert("Accept", "text/html".parse().unwrap());
headers.insert("User-Agent", "Mozilla/5.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
#[test]
fn test_x_awp_channel_case_insensitive() {
let mut headers = HeaderMap::new();
headers.insert("X-AWP-Channel", "Agent".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
#[test]
fn test_agent_accept_and_ua() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/json".parse().unwrap());
headers.insert("User-Agent", "GPT-Agent/1.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
#[test]
fn test_agent_ld_json_accept() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/ld+json".parse().unwrap());
headers.insert("User-Agent", "ClaudeBot/1.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
#[test]
fn test_agent_awp_json_accept() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/awp+json".parse().unwrap());
headers.insert("User-Agent", "my-bot/1.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
#[test]
fn test_human_html_accept() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "text/html".parse().unwrap());
headers.insert("User-Agent", "Mozilla/5.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Human);
}
#[test]
fn test_agent_ua_but_html_accept_is_human() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "text/html".parse().unwrap());
headers.insert("User-Agent", "Googlebot/2.1".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Human);
}
#[test]
fn test_json_accept_but_human_ua_is_human() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/json".parse().unwrap());
headers.insert("User-Agent", "Mozilla/5.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Human);
}
#[test]
fn test_no_headers_is_human() {
let headers = HeaderMap::new();
assert_eq!(detect_requester_type(&headers), RequesterType::Human);
}
#[test]
fn test_anthropic_ua_pattern() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/json".parse().unwrap());
headers.insert("User-Agent", "Anthropic-AI/1.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
#[test]
fn test_openai_ua_pattern() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/json".parse().unwrap());
headers.insert("User-Agent", "OpenAI-Agent/1.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
#[test]
fn test_spider_ua_pattern() {
let mut headers = HeaderMap::new();
headers.insert("Accept", "application/json".parse().unwrap());
headers.insert("User-Agent", "WebSpider/3.0".parse().unwrap());
assert_eq!(detect_requester_type(&headers), RequesterType::Agent);
}
}