use crate::http;
use std::collections::HashMap;
use std::time::Instant;
#[derive(Debug, Clone, PartialEq)]
pub enum HeaderSource {
Http1Line,
Http2PseudoHeader,
Http2Header,
Http3Header,
}
#[derive(Debug, Clone, PartialEq)]
pub struct HttpHeader {
pub name: String,
pub value: Option<String>,
pub position: usize,
pub source: HeaderSource,
}
impl HttpHeader {
pub fn new(name: &str, value: Option<&str>, position: usize, source: HeaderSource) -> Self {
Self { name: name.to_string(), value: value.map(String::from), position, source }
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct HttpCookie {
pub name: String,
pub value: Option<String>,
pub position: usize,
}
#[derive(Debug, Clone)]
pub struct ParsingMetadata {
pub header_count: usize,
pub duplicate_headers: Vec<String>,
pub case_variations: HashMap<String, Vec<String>>,
pub parsing_time_ns: u64,
pub has_malformed_headers: bool,
pub request_line_length: usize,
pub total_headers_length: usize,
}
impl ParsingMetadata {
pub fn new() -> Self {
Self {
header_count: 0,
duplicate_headers: Vec::new(),
case_variations: HashMap::new(),
parsing_time_ns: 0,
has_malformed_headers: false,
request_line_length: 0,
total_headers_length: 0,
}
}
pub fn with_timing<F, R>(mut self, f: F) -> (R, Self)
where
F: FnOnce() -> R,
{
let start = Instant::now();
let result = f();
self.parsing_time_ns = start.elapsed().as_nanos() as u64;
(result, self)
}
}
impl Default for ParsingMetadata {
fn default() -> Self {
Self::new()
}
}
use crate::observable::{ObservableHttpRequest, ObservableHttpResponse};
pub trait HttpParser {
fn supported_version(&self) -> http::Version;
fn can_parse(&self, data: &[u8]) -> bool;
fn name(&self) -> &'static str;
fn parse_request(&self, data: &[u8]) -> Option<ObservableHttpRequest>;
fn parse_response(&self, data: &[u8]) -> Option<ObservableHttpResponse>;
}
pub trait HttpProcessor {
fn can_process_request(&self, data: &[u8]) -> bool;
fn can_process_response(&self, data: &[u8]) -> bool;
fn has_complete_data(&self, data: &[u8]) -> bool;
fn process_request(
&self,
data: &[u8],
) -> Result<Option<ObservableHttpRequest>, crate::error::HuginnNetHttpError>;
fn process_response(
&self,
data: &[u8],
) -> Result<Option<ObservableHttpResponse>, crate::error::HuginnNetHttpError>;
fn supported_version(&self) -> http::Version;
fn name(&self) -> &'static str;
}
pub fn get_diagnostic(
user_agent: Option<String>,
ua_matcher: Option<(&str, Option<&str>)>,
signature_os_matcher: Option<&huginn_net_db::Label>,
) -> http::HttpDiagnosis {
match user_agent {
None => http::HttpDiagnosis::Anonymous,
Some(_ua) => match (ua_matcher, signature_os_matcher) {
(Some((ua_name_db, _ua_flavor_db)), Some(signature_label_db)) => {
if ua_name_db.eq_ignore_ascii_case(&signature_label_db.name) {
http::HttpDiagnosis::Generic
} else {
http::HttpDiagnosis::Dishonest
}
}
_ => http::HttpDiagnosis::None,
},
}
}