eggress_protocol_http/
detect.rs1use eggress_core::detect::{DetectResult, ProtocolDetector};
2use eggress_core::ProtocolId;
3
4pub struct HttpDetector;
8
9const HTTP_METHODS: &[&[u8]] = &[
10 b"GET ",
11 b"POST ",
12 b"PUT ",
13 b"DELETE ",
14 b"HEAD ",
15 b"OPTIONS ",
16 b"PATCH ",
17 b"CONNECT ",
18 b"TRACE ",
19];
20
21impl ProtocolDetector for HttpDetector {
22 fn id(&self) -> ProtocolId {
23 ProtocolId::Http
24 }
25
26 fn detect(&self, prefix: &[u8]) -> DetectResult {
27 if prefix.is_empty() {
28 return DetectResult::NeedMore { minimum: 1 };
29 }
30
31 if prefix.starts_with(b"HTTP/") {
33 if prefix.len() >= 5 {
34 return DetectResult::Match { confidence: 95 };
35 }
36 return DetectResult::NeedMore { minimum: 5 };
37 }
38
39 if b"HTTP/".starts_with(prefix) {
41 return DetectResult::NeedMore { minimum: 5 };
42 }
43
44 for method in HTTP_METHODS {
46 if prefix.starts_with(method) {
47 return DetectResult::Match { confidence: 100 };
48 }
49 if method.starts_with(prefix) {
51 return DetectResult::NeedMore {
52 minimum: method.len(),
53 };
54 }
55 }
56
57 DetectResult::NoMatch
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 #[test]
66 fn test_http_get_match() {
67 let detector = HttpDetector;
68 assert_eq!(
69 detector.detect(b"GET / HTTP/1.1\r\n"),
70 DetectResult::Match { confidence: 100 }
71 );
72 }
73
74 #[test]
75 fn test_http_connect_match() {
76 let detector = HttpDetector;
77 assert_eq!(
78 detector.detect(b"CONNECT example.com:443 HTTP/1.1\r\n"),
79 DetectResult::Match { confidence: 100 }
80 );
81 }
82
83 #[test]
84 fn test_http_response_match() {
85 let detector = HttpDetector;
86 assert_eq!(
87 detector.detect(b"HTTP/1.1 200 OK\r\n"),
88 DetectResult::Match { confidence: 95 }
89 );
90 }
91
92 #[test]
93 fn test_http_need_more() {
94 let detector = HttpDetector;
95 assert_eq!(
96 detector.detect(b"GE"),
97 DetectResult::NeedMore { minimum: 4 }
98 );
99 }
100
101 #[test]
102 fn test_http_empty() {
103 let detector = HttpDetector;
104 assert_eq!(detector.detect(b""), DetectResult::NeedMore { minimum: 1 });
105 }
106
107 #[test]
108 fn test_http_no_match() {
109 let detector = HttpDetector;
110 assert_eq!(detector.detect(b"\x05"), DetectResult::NoMatch);
111 assert_eq!(detector.detect(b"\x04"), DetectResult::NoMatch);
112 }
113
114 #[test]
115 fn test_http_post_match() {
116 let detector = HttpDetector;
117 assert_eq!(
118 detector.detect(b"POST /api HTTP/1.1\r\n"),
119 DetectResult::Match { confidence: 100 }
120 );
121 }
122
123 #[test]
124 fn test_http_partial_method() {
125 let detector = HttpDetector;
126 assert!(matches!(
128 detector.detect(b"P"),
129 DetectResult::NeedMore { .. }
130 ));
131 }
132
133 #[test]
134 fn test_http_partial_response_prefix_needs_more() {
135 let detector = HttpDetector;
136 assert!(matches!(
139 detector.detect(b"H"),
140 DetectResult::NeedMore { minimum: 5 }
141 ));
142 assert!(matches!(
143 detector.detect(b"HT"),
144 DetectResult::NeedMore { minimum: 5 }
145 ));
146 assert!(matches!(
147 detector.detect(b"HTT"),
148 DetectResult::NeedMore { minimum: 5 }
149 ));
150 assert!(matches!(
151 detector.detect(b"HTTP"),
152 DetectResult::NeedMore { minimum: 5 }
153 ));
154 }
155}