iris_core/protocols/stream/http/
parser.rs1use super::transaction::{HttpRequest, HttpResponse};
8use super::Http;
9use crate::conntrack::pdu::L4Pdu;
10use crate::protocols::stream::{
11 ConnParsable, ParseResult, ParsingState, ProbeResult, Session, SessionData,
12};
13
14use httparse::{Request, EMPTY_HEADER};
15use std::collections::HashMap;
16
17#[derive(Default, Debug)]
18pub struct HttpParser {
19 pending: HashMap<usize, Http>,
21 current_trans: usize,
23 cnt: usize,
25 last_body_offset: Option<usize>,
27}
28
29impl HttpParser {
30 pub(crate) fn process_ctos(&mut self, data: &[u8]) -> ParseResult {
32 if let Ok(request) = HttpRequest::parse_from(data) {
33 let session_id = self.cnt;
34 let http = Http {
35 request,
36 response: HttpResponse::default(),
37 trans_depth: session_id,
38 };
39 self.cnt += 1;
40 self.pending.insert(session_id, http);
41 ParseResult::Continue(session_id)
42 } else {
43 ParseResult::Skipped
46 }
47 }
48
49 pub(crate) fn process_stoc(&mut self, data: &[u8], pdu: &L4Pdu) -> ParseResult {
51 if let Ok((response, consumed)) = HttpResponse::parse_from(data) {
52 if let Some(http) = self.pending.get_mut(&self.current_trans) {
53 http.response = response;
54 if consumed < data.len() && consumed > 0 {
56 self.last_body_offset = Some(consumed);
57 }
58 ParseResult::HeadersDone(self.current_trans)
61 } else {
62 log::warn!("HTTP response without oustanding request: {:?}", pdu.ctxt);
63 ParseResult::Skipped
64 }
65 } else {
66 ParseResult::Skipped
69 }
70 }
71}
72
73impl ConnParsable for HttpParser {
74 fn parse(&mut self, pdu: &L4Pdu) -> ParseResult {
75 let offset = pdu.offset();
76 let length = pdu.length();
77 if length == 0 {
78 return ParseResult::Skipped;
79 }
80
81 if let Ok(data) = (pdu.mbuf_ref()).get_data_slice(offset, length) {
82 if pdu.dir {
83 self.process_ctos(data)
84 } else {
85 self.process_stoc(data, pdu)
86 }
87 } else {
88 log::warn!("Malformed packet on parse");
89 ParseResult::Skipped
90 }
91 }
92
93 fn probe(&self, pdu: &L4Pdu) -> ProbeResult {
94 const NUM_OF_HEADERS: usize = 4;
98
99 if pdu.length() < 6 {
100 return ProbeResult::Unsure;
101 }
102 let offset = pdu.offset();
103 let length = pdu.length();
104 if let Ok(data) = (pdu.mbuf_ref()).get_data_slice(offset, length) {
105 match &data[..4] {
107 b"OPTI" | b"GET " | b"HEAD" | b"POST" | b"PUT " | b"PATC" | b"COPY" | b"MOVE"
108 | b"DELE" | b"LINK" | b"UNLI" | b"TRAC" | b"WRAP" => (),
109 _ => return ProbeResult::NotForUs,
110 }
111 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
113 let mut req = Request::new(&mut headers[..]);
114 let status = req.parse(data);
115 if let Err(e) = status {
116 if e != httparse::Error::TooManyHeaders {
117 log::trace!(
118 "data could be HTTP, but got error {:?} while parsing",
119 status
120 );
121 return ProbeResult::Unsure;
122 }
123 }
124 ProbeResult::Certain
125 } else {
126 log::warn!("Malformed packet");
127 ProbeResult::Error
128 }
129 }
130
131 fn remove_session(&mut self, session_id: usize) -> Option<Session> {
132 self.current_trans = session_id + 1;
134 self.pending.remove(&session_id).map(|http| Session {
135 data: SessionData::Http(Box::new(http)),
136 id: session_id,
137 })
138 }
139
140 fn drain_sessions(&mut self) -> Vec<Session> {
141 self.pending
142 .drain()
143 .map(|(session_id, http)| Session {
144 data: SessionData::Http(Box::new(http)),
145 id: session_id,
146 })
147 .collect()
148 }
149
150 fn session_parsed_state(&self) -> ParsingState {
151 ParsingState::Parsing
152 }
153
154 fn body_offset(&mut self) -> Option<usize> {
155 std::mem::take(&mut self.last_body_offset)
156 }
157}