huginn_net_http/
http_common.rs1use crate::http;
2use std::collections::HashMap;
3use std::time::Instant;
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum HeaderSource {
7 Http1Line,
8 Http2PseudoHeader,
9 Http2Header,
10 Http3Header,
11}
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct HttpHeader {
16 pub name: String,
17 pub value: Option<String>,
18 pub position: usize,
20 pub source: HeaderSource,
22}
23
24impl HttpHeader {
25 pub fn new(name: &str, value: Option<&str>, position: usize, source: HeaderSource) -> Self {
26 Self { name: name.to_string(), value: value.map(String::from), position, source }
27 }
28}
29
30#[derive(Debug, Clone, PartialEq)]
32pub struct HttpCookie {
33 pub name: String,
34 pub value: Option<String>,
35 pub position: usize,
37}
38
39#[derive(Debug, Clone)]
41pub struct ParsingMetadata {
42 pub header_count: usize,
43 pub duplicate_headers: Vec<String>,
44 pub case_variations: HashMap<String, Vec<String>>,
45 pub parsing_time_ns: u64,
46 pub has_malformed_headers: bool,
47 pub request_line_length: usize,
48 pub total_headers_length: usize,
49}
50
51impl ParsingMetadata {
52 pub fn new() -> Self {
53 Self {
54 header_count: 0,
55 duplicate_headers: Vec::new(),
56 case_variations: HashMap::new(),
57 parsing_time_ns: 0,
58 has_malformed_headers: false,
59 request_line_length: 0,
60 total_headers_length: 0,
61 }
62 }
63
64 pub fn with_timing<F, R>(mut self, f: F) -> (R, Self)
65 where
66 F: FnOnce() -> R,
67 {
68 let start = Instant::now();
69 let result = f();
70 self.parsing_time_ns = start.elapsed().as_nanos() as u64;
71 (result, self)
72 }
73}
74
75impl Default for ParsingMetadata {
76 fn default() -> Self {
77 Self::new()
78 }
79}
80
81use crate::observable::{ObservableHttpRequest, ObservableHttpResponse};
82
83pub trait HttpParser {
85 fn supported_version(&self) -> http::Version;
87
88 fn can_parse(&self, data: &[u8]) -> bool;
90
91 fn name(&self) -> &'static str;
93
94 fn parse_request(&self, data: &[u8]) -> Option<ObservableHttpRequest>;
97
98 fn parse_response(&self, data: &[u8]) -> Option<ObservableHttpResponse>;
101}
102
103pub trait HttpProcessor {
105 fn can_process_request(&self, data: &[u8]) -> bool;
107
108 fn can_process_response(&self, data: &[u8]) -> bool;
110
111 fn has_complete_data(&self, data: &[u8]) -> bool;
113
114 fn process_request(
116 &self,
117 data: &[u8],
118 ) -> Result<Option<ObservableHttpRequest>, crate::error::HuginnNetHttpError>;
119
120 fn process_response(
122 &self,
123 data: &[u8],
124 ) -> Result<Option<ObservableHttpResponse>, crate::error::HuginnNetHttpError>;
125
126 fn supported_version(&self) -> http::Version;
128
129 fn name(&self) -> &'static str;
131}
132
133pub fn get_diagnostic(
149 user_agent: Option<String>,
150 ua_matcher: Option<(&String, &Option<String>)>,
151 signature_os_matcher: Option<&huginn_net_db::Label>,
152) -> http::HttpDiagnosis {
153 match user_agent {
154 None => http::HttpDiagnosis::Anonymous,
155 Some(_ua) => match (ua_matcher, signature_os_matcher) {
156 (Some((ua_name_db, _ua_flavor_db)), Some(signature_label_db)) => {
157 if ua_name_db.eq_ignore_ascii_case(&signature_label_db.name) {
158 http::HttpDiagnosis::Generic
159 } else {
160 http::HttpDiagnosis::Dishonest
161 }
162 }
163 _ => http::HttpDiagnosis::None,
164 },
165 }
166}