Skip to main content

http_net/
preface.rs

1//! Detection of the HTTP/2 connection preface (RFC 9113 section 3.4).
2
3/// The 24 bytes an HTTP/2 client sends first when it knows the server
4/// speaks HTTP/2 ("prior knowledge").
5pub(crate) const H2_PREFACE: &[u8; 24] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
6
7/// Whether `input` starts with the HTTP/2 connection preface:
8/// `Some(true)` when the first 24 bytes are the preface, `Some(false)` as
9/// soon as a byte differs from it, and `None` while `input` is a shorter,
10/// so far matching prefix (read more to decide). At most 24 compares.
11///
12/// [`ServerConn`](crate::ServerConn) checks this at the start of every
13/// connection and refuses the preface with
14/// [`HttpError::Http2Preface`](crate::HttpError::Http2Preface); a driver
15/// that accepts prior-knowledge HTTP/2 (behind a trusted proxy only) checks
16/// it first and routes the connection to an HTTP/2 engine.
17///
18/// # Examples
19///
20/// ```
21/// use http_net::is_h2_preface;
22///
23/// assert_eq!(is_h2_preface(b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n\x00\x00"), Some(true));
24/// assert_eq!(is_h2_preface(b"PRI * HTTP/2"), None);
25/// assert_eq!(is_h2_preface(b"GET / HTTP/1.1\r\n"), Some(false));
26/// assert_eq!(is_h2_preface(b""), None);
27/// ```
28#[must_use]
29pub fn is_h2_preface(input: &[u8]) -> Option<bool> {
30    let n = input.len().min(H2_PREFACE.len());
31    if input[..n] != H2_PREFACE[..n] {
32        return Some(false);
33    }
34    if n == H2_PREFACE.len() {
35        Some(true)
36    } else {
37        None
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use alloc::vec::Vec;
44
45    use super::*;
46
47    #[test]
48    fn test_is_h2_preface_every_prefix_length() {
49        for len in 0..H2_PREFACE.len() {
50            assert_eq!(is_h2_preface(&H2_PREFACE[..len]), None, "{len}");
51        }
52        assert_eq!(is_h2_preface(H2_PREFACE), Some(true));
53        let mut more = H2_PREFACE.to_vec();
54        more.extend_from_slice(b"\x00\x00\x12\x04");
55        assert_eq!(is_h2_preface(&more), Some(true));
56    }
57
58    #[test]
59    fn test_is_h2_preface_any_differing_byte_is_false() {
60        for at in 0..H2_PREFACE.len() {
61            let mut bad: Vec<u8> = H2_PREFACE.to_vec();
62            bad[at] ^= 0x20;
63            assert_eq!(is_h2_preface(&bad), Some(false), "{at}");
64            // Decided as soon as the differing byte is seen.
65            assert_eq!(is_h2_preface(&bad[..=at]), Some(false), "{at}");
66        }
67        assert_eq!(is_h2_preface(b"PRI * HTTP/1.1\r\n\r\n"), Some(false));
68    }
69}