huginn_net_http/http/
common.rs1use super::{HttpDiagnosis, Version};
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 super::observable::{ObservableHttpRequest, ObservableHttpResponse};
82
83pub trait HttpParser {
85 fn supported_version(&self) -> 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) -> Version;
128
129 fn name(&self) -> &'static str;
131}
132
133pub fn get_diagnostic(
153 user_agent: Option<String>,
154 ua_os_family: Option<&str>,
155 network_os_name: Option<&str>,
156) -> HttpDiagnosis {
157 match user_agent {
158 None => HttpDiagnosis::Anonymous,
159 Some(_ua) => match (ua_os_family, network_os_name) {
160 (Some(ua_name), Some(net_name)) => {
161 if ua_name.eq_ignore_ascii_case(net_name) {
162 HttpDiagnosis::Generic
163 } else {
164 HttpDiagnosis::Dishonest
165 }
166 }
167 _ => HttpDiagnosis::None,
168 },
169 }
170}