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 {
27 name: name.to_string(),
28 value: value.map(String::from),
29 position,
30 source,
31 }
32 }
33}
34
35#[derive(Debug, Clone, PartialEq)]
37pub struct HttpCookie {
38 pub name: String,
39 pub value: Option<String>,
40 pub position: usize,
42}
43
44#[derive(Debug, Clone)]
46pub struct ParsingMetadata {
47 pub header_count: usize,
48 pub duplicate_headers: Vec<String>,
49 pub case_variations: HashMap<String, Vec<String>>,
50 pub parsing_time_ns: u64,
51 pub has_malformed_headers: bool,
52 pub request_line_length: usize,
53 pub total_headers_length: usize,
54}
55
56impl ParsingMetadata {
57 pub fn new() -> Self {
58 Self {
59 header_count: 0,
60 duplicate_headers: Vec::new(),
61 case_variations: HashMap::new(),
62 parsing_time_ns: 0,
63 has_malformed_headers: false,
64 request_line_length: 0,
65 total_headers_length: 0,
66 }
67 }
68
69 pub fn with_timing<F, R>(mut self, f: F) -> (R, Self)
70 where
71 F: FnOnce() -> R,
72 {
73 let start = Instant::now();
74 let result = f();
75 self.parsing_time_ns = start.elapsed().as_nanos() as u64;
76 (result, self)
77 }
78}
79
80impl Default for ParsingMetadata {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86use crate::observable::{ObservableHttpRequest, ObservableHttpResponse};
87
88pub trait HttpParser {
90 fn supported_version(&self) -> http::Version;
92
93 fn can_parse(&self, data: &[u8]) -> bool;
95
96 fn name(&self) -> &'static str;
98
99 fn parse_request(&self, data: &[u8]) -> Option<ObservableHttpRequest>;
102
103 fn parse_response(&self, data: &[u8]) -> Option<ObservableHttpResponse>;
106}
107
108pub trait HttpProcessor {
110 fn can_process_request(&self, data: &[u8]) -> bool;
112
113 fn can_process_response(&self, data: &[u8]) -> bool;
115
116 fn has_complete_data(&self, data: &[u8]) -> bool;
118
119 fn process_request(
121 &self,
122 data: &[u8],
123 ) -> Result<Option<ObservableHttpRequest>, crate::error::HuginnNetHttpError>;
124
125 fn process_response(
127 &self,
128 data: &[u8],
129 ) -> Result<Option<ObservableHttpResponse>, crate::error::HuginnNetHttpError>;
130
131 fn supported_version(&self) -> http::Version;
133
134 fn name(&self) -> &'static str;
136}
137
138pub fn get_diagnostic(
154 user_agent: Option<String>,
155 ua_matcher: Option<(&String, &Option<String>)>,
156 signature_os_matcher: Option<&huginn_net_db::Label>,
157) -> http::HttpDiagnosis {
158 match user_agent {
159 None => http::HttpDiagnosis::Anonymous,
160 Some(_ua) => match (ua_matcher, signature_os_matcher) {
161 (Some((ua_name_db, _ua_flavor_db)), Some(signature_label_db)) => {
162 if ua_name_db.eq_ignore_ascii_case(&signature_label_db.name) {
163 http::HttpDiagnosis::Generic
164 } else {
165 http::HttpDiagnosis::Dishonest
166 }
167 }
168 _ => http::HttpDiagnosis::None,
169 },
170 }
171}