http_handle/
protocol_state.rs1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum ProtocolClassification {
20 Http2Preface,
22 TlsLike,
24 Unknown,
26}
27
28const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
29
30pub fn classify_protocol_bytes(input: &[u8]) -> ProtocolClassification {
44 if input.is_empty() {
45 return ProtocolClassification::Unknown;
46 }
47 if H2_PREFACE.starts_with(input) || input.starts_with(H2_PREFACE) {
48 return ProtocolClassification::Http2Preface;
49 }
50 if is_tls_like(input) {
51 return ProtocolClassification::TlsLike;
52 }
53 ProtocolClassification::Unknown
54}
55
56fn is_tls_like(input: &[u8]) -> bool {
57 if input.len() < 5 {
58 return false;
59 }
60 let content_type = input[0];
61 let version_major = input[1];
62 matches!(content_type, 20..=23) && version_major == 3
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn classifies_http2_preface() {
71 assert_eq!(
72 classify_protocol_bytes(H2_PREFACE),
73 ProtocolClassification::Http2Preface
74 );
75 }
76
77 #[test]
78 fn classifies_tls_like() {
79 let tls = [22_u8, 3, 3, 0, 42, 1, 0, 0, 38];
80 assert_eq!(
81 classify_protocol_bytes(&tls),
82 ProtocolClassification::TlsLike
83 );
84 }
85
86 #[test]
87 fn unknown_for_random_bytes() {
88 let data = [1_u8, 2, 3, 4, 5, 6];
89 assert_eq!(
90 classify_protocol_bytes(&data),
91 ProtocolClassification::Unknown
92 );
93 }
94
95 #[test]
96 fn unknown_for_empty_or_short_frames() {
97 assert_eq!(
98 classify_protocol_bytes(&[]),
99 ProtocolClassification::Unknown
100 );
101 assert_eq!(
102 classify_protocol_bytes(&[22, 3, 1, 0]),
103 ProtocolClassification::Unknown
104 );
105 }
106
107 #[test]
108 fn classifies_http2_when_input_is_prefix() {
109 assert_eq!(
110 classify_protocol_bytes(&H2_PREFACE[..8]),
111 ProtocolClassification::Http2Preface
112 );
113 }
114}