ocular_protocol/
handlers.rs1use crate::handler::ProtocolHandler;
2use crate::ProxyEvent;
3
4pub 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
17pub 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
49pub 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
72pub 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
89pub 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
108pub 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}