Skip to main content

micro_h2/hpack/
static_table.rs

1//! HPACK's static table (RFC 7541 Appendix A).
2//!
3//! Sixty-one entries every HTTP/2 endpoint is assumed to know. Index 0 is
4//! unused, so the table is 1-based, and dynamic-table entries continue from 62 —
5//! an off-by-one here shifts every header the server sends.
6
7/// `(name, value)`. An empty value means the entry names a header whose value
8/// must be given literally.
9pub const ENTRIES: [(&str, &str); 61] = [
10    (":authority", ""),
11    (":method", "GET"),
12    (":method", "POST"),
13    (":path", "/"),
14    (":path", "/index.html"),
15    (":scheme", "http"),
16    (":scheme", "https"),
17    (":status", "200"),
18    (":status", "204"),
19    (":status", "206"),
20    (":status", "304"),
21    (":status", "400"),
22    (":status", "404"),
23    (":status", "500"),
24    ("accept-charset", ""),
25    ("accept-encoding", "gzip, deflate"),
26    ("accept-language", ""),
27    ("accept-ranges", ""),
28    ("accept", ""),
29    ("access-control-allow-origin", ""),
30    ("age", ""),
31    ("allow", ""),
32    ("authorization", ""),
33    ("cache-control", ""),
34    ("content-disposition", ""),
35    ("content-encoding", ""),
36    ("content-language", ""),
37    ("content-length", ""),
38    ("content-location", ""),
39    ("content-range", ""),
40    ("content-type", ""),
41    ("cookie", ""),
42    ("date", ""),
43    ("etag", ""),
44    ("expect", ""),
45    ("expires", ""),
46    ("from", ""),
47    ("host", ""),
48    ("if-match", ""),
49    ("if-modified-since", ""),
50    ("if-none-match", ""),
51    ("if-range", ""),
52    ("if-unmodified-since", ""),
53    ("last-modified", ""),
54    ("link", ""),
55    ("location", ""),
56    ("max-forwards", ""),
57    ("proxy-authenticate", ""),
58    ("proxy-authorization", ""),
59    ("range", ""),
60    ("referer", ""),
61    ("refresh", ""),
62    ("retry-after", ""),
63    ("server", ""),
64    ("set-cookie", ""),
65    ("strict-transport-security", ""),
66    ("transfer-encoding", ""),
67    ("user-agent", ""),
68    ("vary", ""),
69    ("via", ""),
70    ("www-authenticate", ""),
71];
72
73/// The first index a dynamic-table entry can take.
74pub const DYNAMIC_BASE: usize = ENTRIES.len() + 1;
75
76/// Look up a 1-based static index.
77pub fn get(index: usize) -> Option<(&'static str, &'static str)> {
78    if index == 0 {
79        return None;
80    }
81    ENTRIES.get(index - 1).copied()
82}
83
84/// Find an entry matching both name and value, for the most compact encoding.
85pub fn find(name: &str, value: &str) -> Option<usize> {
86    ENTRIES
87        .iter()
88        .position(|(n, v)| *n == name && *v == value)
89        .map(|i| i + 1)
90}
91
92/// Find an entry matching the name alone.
93pub fn find_name(name: &str) -> Option<usize> {
94    ENTRIES.iter().position(|(n, _)| *n == name).map(|i| i + 1)
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn the_table_is_the_length_and_shape_the_rfc_defines() {
103        assert_eq!(ENTRIES.len(), 61);
104        assert_eq!(DYNAMIC_BASE, 62);
105        // Boundaries, where an off-by-one would show up.
106        assert_eq!(get(1), Some((":authority", "")));
107        assert_eq!(get(2), Some((":method", "GET")));
108        assert_eq!(get(61), Some(("www-authenticate", "")));
109        assert_eq!(get(62), None);
110        // Index 0 is not an entry: the encoding uses it as a marker.
111        assert_eq!(get(0), None);
112    }
113
114    #[test]
115    fn lookups_find_the_indices_the_rfc_examples_use() {
116        assert_eq!(find(":method", "GET"), Some(2));
117        assert_eq!(find(":path", "/"), Some(4));
118        assert_eq!(find(":scheme", "http"), Some(6));
119        assert_eq!(find(":status", "200"), Some(8));
120        assert_eq!(find_name(":authority"), Some(1));
121        assert_eq!(find_name("content-type"), Some(31));
122        assert_eq!(find(":method", "PUT"), None);
123    }
124}