1use crate::huffman;
4
5pub static STATIC_TABLE: [(&str, &str); 99] = [
7 (":authority", ""),
8 (":path", "/"),
9 ("age", "0"),
10 ("content-disposition", ""),
11 ("content-length", "0"),
12 ("cookie", ""),
13 ("date", ""),
14 ("etag", ""),
15 ("if-modified-since", ""),
16 ("if-none-match", ""),
17 ("last-modified", ""),
18 ("link", ""),
19 ("location", ""),
20 ("referer", ""),
21 ("set-cookie", ""),
22 (":method", "CONNECT"),
23 (":method", "DELETE"),
24 (":method", "GET"),
25 (":method", "HEAD"),
26 (":method", "OPTIONS"),
27 (":method", "POST"),
28 (":method", "PUT"),
29 (":scheme", "http"),
30 (":scheme", "https"),
31 (":status", "103"),
32 (":status", "200"),
33 (":status", "304"),
34 (":status", "404"),
35 (":status", "503"),
36 ("accept", "*/*"),
37 ("accept", "application/dns-message"),
38 ("accept-encoding", "gzip, deflate, br"),
39 ("accept-ranges", "bytes"),
40 ("access-control-allow-headers", "cache-control"),
41 ("access-control-allow-headers", "content-type"),
42 ("access-control-allow-origin", "*"),
43 ("cache-control", "max-age=0"),
44 ("cache-control", "max-age=2592000"),
45 ("cache-control", "max-age=604800"),
46 ("cache-control", "no-cache"),
47 ("cache-control", "no-store"),
48 ("cache-control", "public, max-age=31536000"),
49 ("content-encoding", "br"),
50 ("content-encoding", "gzip"),
51 ("content-type", "application/dns-message"),
52 ("content-type", "application/javascript"),
53 ("content-type", "application/json"),
54 ("content-type", "application/x-www-form-urlencoded"),
55 ("content-type", "image/gif"),
56 ("content-type", "image/jpeg"),
57 ("content-type", "image/png"),
58 ("content-type", "text/css"),
59 ("content-type", "text/html; charset=utf-8"),
60 ("content-type", "text/plain"),
61 ("content-type", "text/plain;charset=utf-8"),
62 ("range", "bytes=0-"),
63 ("strict-transport-security", "max-age=31536000"),
64 ("strict-transport-security", "max-age=31536000; includesubdomains"),
65 ("strict-transport-security", "max-age=31536000; includesubdomains; preload"),
66 ("vary", "accept-encoding"),
67 ("vary", "origin"),
68 ("x-content-type-options", "nosniff"),
69 ("x-xss-protection", "1; mode=block"),
70 (":status", "100"),
71 (":status", "204"),
72 (":status", "206"),
73 (":status", "302"),
74 (":status", "400"),
75 (":status", "403"),
76 (":status", "421"),
77 (":status", "425"),
78 (":status", "500"),
79 ("accept-language", ""),
80 ("access-control-allow-credentials", "FALSE"),
81 ("access-control-allow-credentials", "TRUE"),
82 ("access-control-allow-headers", "*"),
83 ("access-control-allow-methods", "get"),
84 ("access-control-allow-methods", "get, post, options"),
85 ("access-control-allow-methods", "options"),
86 ("access-control-expose-headers", "content-length"),
87 ("access-control-request-headers", "content-type"),
88 ("access-control-request-method", "get"),
89 ("access-control-request-method", "post"),
90 ("alt-svc", "clear"),
91 ("authorization", ""),
92 ("content-security-policy", "script-src 'none'; object-src 'none'; base-uri 'none'"),
93 ("early-data", "1"),
94 ("expect-ct", ""),
95 ("forwarded", ""),
96 ("if-range", ""),
97 ("origin", ""),
98 ("purpose", "prefetch"),
99 ("server", ""),
100 ("timing-allow-origin", "*"),
101 ("upgrade-insecure-requests", "1"),
102 ("user-agent", ""),
103 ("x-forwarded-for", ""),
104 ("x-frame-options", "deny"),
105 ("x-frame-options", "sameorigin"),
106];
107
108pub(crate) fn static_find(name: &[u8], value: &[u8]) -> (Option<u8>, Option<u8>) {
110 let mut name_idx = None;
111 for (i, (n, v)) in STATIC_TABLE.iter().enumerate() {
112 if n.len() == name.len() && n.as_bytes() == name {
113 if v.as_bytes() == value {
114 return (Some(i as u8), Some(i as u8));
115 }
116 name_idx.get_or_insert(i as u8);
117 }
118 }
119 (None, name_idx)
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub(crate) enum Short {
125 Incomplete,
126 Invalid,
127}
128
129pub const MAX_INT: u64 = (1 << 62) - 1;
131
132pub(crate) fn get_int(buf: &[u8], pos: &mut usize, n: u8) -> Result<u64, Short> {
134 let b = *buf.get(*pos).ok_or(Short::Incomplete)?;
135 *pos += 1;
136 let max = (1u64 << n) - 1;
137 let mut v = b as u64 & max;
138 if v < max {
139 return Ok(v);
140 }
141 let mut shift = 0u32;
142 loop {
143 let b = *buf.get(*pos).ok_or(Short::Incomplete)?;
144 *pos += 1;
145 if shift > 56 {
146 return Err(Short::Invalid);
147 }
148 v += ((b & 0x7f) as u64) << shift;
149 if v > MAX_INT {
150 return Err(Short::Invalid);
151 }
152 if b & 0x80 == 0 {
153 return Ok(v);
154 }
155 shift += 7;
156 }
157}
158
159pub fn put_int(out: &mut Vec<u8>, flags: u8, n: u8, mut v: u64) {
161 let max = (1u64 << n) - 1;
162 if v < max {
163 out.push(flags | v as u8);
164 return;
165 }
166 out.push(flags | max as u8);
167 v -= max;
168 while v >= 0x80 {
169 out.push((v as u8 & 0x7f) | 0x80);
170 v >>= 7;
171 }
172 out.push(v as u8);
173}
174
175#[derive(Clone, Copy)]
177pub(crate) struct Span {
178 pub huff: bool,
179 pub start: usize,
180 pub end: usize,
181}
182
183pub(crate) fn get_span(buf: &[u8], pos: &mut usize, n: u8) -> Result<Span, Short> {
186 let huff = *buf.get(*pos).ok_or(Short::Incomplete)? & (1 << n) != 0;
187 let len = get_int(buf, pos, n)?;
188 let start = *pos;
189 let avail = (buf.len() - start) as u64;
190 if len > avail {
191 return Err(Short::Incomplete);
192 }
193 *pos = start + len as usize;
194 Ok(Span { huff, start, end: *pos })
195}
196
197pub(crate) fn span_bytes(buf: &[u8], s: Span) -> Result<Vec<u8>, Short> {
199 let raw = &buf[s.start..s.end];
200 if s.huff {
201 let mut out = Vec::new();
202 huffman::decode(raw, &mut out).map_err(|_| Short::Invalid)?;
203 Ok(out)
204 } else {
205 Ok(raw.to_vec())
206 }
207}
208
209pub(crate) fn put_str(out: &mut Vec<u8>, flags: u8, n: u8, s: &[u8]) {
212 let hlen = huffman::encoded_len(s);
213 if hlen < s.len() {
214 put_int(out, flags | (1 << n), n, hlen as u64);
215 huffman::encode(s, out);
216 } else {
217 put_int(out, flags, n, s.len() as u64);
218 out.extend_from_slice(s);
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225
226 #[test]
227 fn static_table_lookup() {
228 assert_eq!(STATIC_TABLE.len(), 99);
229 assert_eq!(static_find(b":path", b"/"), (Some(1), Some(1)));
230 assert_eq!(static_find(b":path", b"/x"), (None, Some(1)));
231 assert_eq!(static_find(b":status", b"500"), (Some(71), Some(71)));
232 assert_eq!(static_find(b"x-frame-options", b"sameorigin"), (Some(98), Some(98)));
233 assert_eq!(static_find(b"x-unknown", b""), (None, None));
234 }
235
236 #[test]
237 fn integers() {
238 let mut out = Vec::new();
240 put_int(&mut out, 0, 5, 10);
241 assert_eq!(out, [10]);
242 out.clear();
243 put_int(&mut out, 0, 5, 1337);
244 assert_eq!(out, [31, 154, 10]);
245 let mut p = 0;
246 assert_eq!(get_int(&out, &mut p, 5), Ok(1337));
247 out.clear();
248 put_int(&mut out, 0, 8, 42);
249 assert_eq!(out, [42]);
250 for &v in &[0u64, 1, 30, 31, 127, 128, 255, 16383, 1 << 40, MAX_INT] {
251 for n in 1..=8u8 {
252 let mut o = vec![];
253 put_int(&mut o, 0, n, v);
254 let mut p = 0;
255 assert_eq!(get_int(&o, &mut p, n), Ok(v));
256 assert_eq!(p, o.len());
257 let mut p = 0;
258 if o.len() > 1 {
259 assert_eq!(get_int(&o[..o.len() - 1], &mut p, n), Err(Short::Incomplete));
260 }
261 }
262 }
263 let mut o = vec![];
265 put_int(&mut o, 0, 6, MAX_INT + 1);
266 let mut p = 0;
267 assert_eq!(get_int(&o, &mut p, 6), Err(Short::Invalid));
268 let o = [0x3f, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01];
270 let mut p = 0;
271 assert_eq!(get_int(&o, &mut p, 6), Err(Short::Invalid));
272 }
273}