Skip to main content

ocular_protocol/
handlers.rs

1use crate::handler::ProtocolHandler;
2use crate::ProxyEvent;
3
4// ─── Redis ──────────────────────────────────────────────────────────────────
5
6pub struct RedisHandler;
7
8impl ProtocolHandler for RedisHandler {
9    fn parse_request(&self, buf: &[u8]) -> Option<String> {
10        crate::parse_resp(buf).ok().flatten().map(|(val, _)| val.to_command_string())
11    }
12    fn parse_response(&self, buf: &[u8]) -> Option<String> {
13        crate::parse_resp(buf).ok().flatten().map(|(val, _)| val.to_command_string())
14    }
15}
16
17// ─── MySQL ──────────────────────────────────────────────────────────────────
18
19pub struct MysqlHandler;
20
21impl ProtocolHandler for MysqlHandler {
22    fn parse_request(&self, buf: &[u8]) -> Option<String> {
23        crate::parse_mysql_request(buf).map(|pkt| pkt.to_summary())
24    }
25    fn extract_full_command(&self, buf: &[u8]) -> Option<String> {
26        if buf.len() < 5 { return None; }
27        let payload_len = (buf[0] as usize) | (buf[1] as usize) << 8 | (buf[2] as usize) << 16;
28        if buf.len() < 4 + payload_len || payload_len <= 1 { return None; }
29        let cmd = buf[4];
30        if cmd == 0x03 || cmd == 0x16 {
31            let sql = String::from_utf8_lossy(&buf[5..4 + payload_len]);
32            Some(sql.replace(|c: char| c.is_control(), ""))
33        } else {
34            self.parse_request(buf)
35        }
36    }
37    fn parse_response(&self, buf: &[u8]) -> Option<String> {
38        crate::parse_mysql_response(buf).map(|r| r.to_summary())
39    }
40    fn format_response_detail(&self, buf: &[u8]) -> Option<String> {
41        crate::parse_mysql_response(buf).map(|r| r.to_display())
42    }
43    fn needs_response_buffering(&self) -> bool { true }
44    fn response_complete(&self, buf: &[u8]) -> bool {
45        crate::mysql::mysql_response_complete(buf)
46    }
47}
48
49// ─── PostgreSQL ─────────────────────────────────────────────────────────────
50
51pub struct PostgresHandler;
52
53impl ProtocolHandler for PostgresHandler {
54    fn parse_request(&self, buf: &[u8]) -> Option<String> {
55        crate::postgres::parse_postgres_request(buf)
56    }
57    fn extract_full_command(&self, buf: &[u8]) -> Option<String> {
58        crate::postgres::extract_postgres_full_command(buf)
59    }
60    fn parse_response(&self, buf: &[u8]) -> Option<String> {
61        crate::postgres::parse_postgres_response(buf)
62    }
63    fn format_response_detail(&self, buf: &[u8]) -> Option<String> {
64        crate::postgres::format_postgres_response_detail(buf)
65    }
66    fn needs_response_buffering(&self) -> bool { true }
67    fn response_complete(&self, buf: &[u8]) -> bool {
68        crate::postgres::postgres_response_complete(buf)
69    }
70}
71
72// ─── AMQP ───────────────────────────────────────────────────────────────────
73
74pub struct AmqpHandler;
75
76impl ProtocolHandler for AmqpHandler {
77    fn parse_request(&self, buf: &[u8]) -> Option<String> {
78        crate::amqp::parse_amqp_request(buf)
79    }
80    fn parse_response(&self, buf: &[u8]) -> Option<String> {
81        crate::amqp::parse_amqp_response(buf)
82    }
83    fn format_response_detail(&self, buf: &[u8]) -> Option<String> {
84        crate::amqp::format_amqp_response_detail(buf)
85    }
86    fn is_frame_based(&self) -> bool { true }
87}
88
89// ─── MongoDB ────────────────────────────────────────────────────────────────
90
91pub struct MongodbHandler;
92
93impl ProtocolHandler for MongodbHandler {
94    fn parse_request(&self, buf: &[u8]) -> Option<String> {
95        crate::mongodb::parse_mongo_request(buf)
96    }
97    fn extract_full_command(&self, buf: &[u8]) -> Option<String> {
98        crate::mongodb::extract_mongo_full_command(buf)
99    }
100    fn parse_response(&self, buf: &[u8]) -> Option<String> {
101        crate::mongodb::parse_mongo_response(buf)
102    }
103    fn format_response_detail(&self, buf: &[u8]) -> Option<String> {
104        crate::mongodb::format_mongo_response_detail(buf)
105    }
106}
107
108// ─── HTTP ───────────────────────────────────────────────────────────────────
109
110pub struct HttpHandler;
111
112impl ProtocolHandler for HttpHandler {
113    fn parse_request(&self, buf: &[u8]) -> Option<String> {
114        crate::http::parse_http_request(buf)
115    }
116    fn extract_full_command(&self, buf: &[u8]) -> Option<String> {
117        crate::http::extract_http_full_command(buf)
118    }
119    fn parse_response(&self, buf: &[u8]) -> Option<String> {
120        crate::http::parse_http_response(buf)
121    }
122    fn format_response_detail(&self, buf: &[u8]) -> Option<String> {
123        crate::http::format_http_response_detail(buf)
124    }
125    fn to_replay_command(&self, ev: &ProxyEvent) -> String {
126        let dest = ev.dest.as_deref().unwrap_or("localhost");
127        let lines: Vec<&str> = ev.full_command.lines().collect();
128        let first_line = lines.first().copied().unwrap_or("");
129        let mut parts = first_line.splitn(2, ' ');
130        let method = parts.next().unwrap_or("GET");
131        let path = parts.next().unwrap_or("/");
132        let url = format!("http://{}{}", dest, path);
133        let mut curl = format!("curl -X {} '{}'", method, url);
134
135        let mut in_headers = false;
136        let mut in_body = false;
137        let mut body = String::new();
138        for line in &lines[1..] {
139            if *line == "[Request Headers]" { in_headers = true; in_body = false; continue; }
140            if *line == "[Request Body]" { in_body = true; in_headers = false; continue; }
141            if line.starts_with('[') && line.ends_with(']') { in_headers = false; in_body = false; continue; }
142            if line.is_empty() { continue; }
143            if in_headers { curl.push_str(&format!(" \\\n  -H '{}'", line)); }
144            if in_body { body.push_str(line); }
145        }
146        if !body.is_empty() { curl.push_str(&format!(" \\\n  -d '{}'", body)); }
147        curl
148    }
149    fn needs_request_buffering(&self) -> bool { true }
150    fn needs_response_buffering(&self) -> bool { true }
151    fn request_complete(&self, buf: &[u8]) -> bool {
152        crate::http::http_request_complete(buf)
153    }
154    fn response_complete(&self, buf: &[u8]) -> bool {
155        crate::http::http_response_complete(buf)
156    }
157}