1use bytes::Bytes;
4use std::collections::VecDeque;
5
6const STATIC_TABLE: [(&[u8], &[u8]); 61] = [
9 (b":authority", b""),
10 (b":method", b"GET"),
11 (b":method", b"POST"),
12 (b":path", b"/"),
13 (b":path", b"/index.html"),
14 (b":scheme", b"http"),
15 (b":scheme", b"https"),
16 (b":status", b"200"),
17 (b":status", b"204"),
18 (b":status", b"206"),
19 (b":status", b"304"),
20 (b":status", b"400"),
21 (b":status", b"404"),
22 (b":status", b"500"),
23 (b"accept-charset", b""),
24 (b"accept-encoding", b"gzip, deflate"),
25 (b"accept-language", b""),
26 (b"accept-ranges", b""),
27 (b"accept", b""),
28 (b"access-control-allow-origin", b""),
29 (b"age", b""),
30 (b"allow", b""),
31 (b"authorization", b""),
32 (b"cache-control", b""),
33 (b"content-disposition", b""),
34 (b"content-encoding", b""),
35 (b"content-language", b""),
36 (b"content-length", b""),
37 (b"content-location", b""),
38 (b"content-range", b""),
39 (b"content-type", b""),
40 (b"cookie", b""),
41 (b"date", b""),
42 (b"etag", b""),
43 (b"expect", b""),
44 (b"expires", b""),
45 (b"from", b""),
46 (b"host", b""),
47 (b"if-match", b""),
48 (b"if-modified-since", b""),
49 (b"if-none-match", b""),
50 (b"if-range", b""),
51 (b"if-unmodified-since", b""),
52 (b"last-modified", b""),
53 (b"link", b""),
54 (b"location", b""),
55 (b"max-forwards", b""),
56 (b"proxy-authenticate", b""),
57 (b"proxy-authorization", b""),
58 (b"range", b""),
59 (b"referer", b""),
60 (b"refresh", b""),
61 (b"retry-after", b""),
62 (b"server", b""),
63 (b"set-cookie", b""),
64 (b"strict-transport-security", b""),
65 (b"transfer-encoding", b""),
66 (b"user-agent", b""),
67 (b"vary", b""),
68 (b"via", b""),
69 (b"www-authenticate", b""),
70];
71
72pub(crate) const STATIC_LEN: usize = 61;
74
75const ENTRY_OVERHEAD: usize = 32;
77
78#[derive(Debug, Clone, PartialEq, Eq)]
80pub struct Header {
81 name: Bytes,
82 value: Bytes,
83 size: usize,
86}
87
88impl Header {
89 #[inline]
93 pub fn new(name: impl Into<Bytes>, value: impl Into<Bytes>) -> Self {
94 let name = name.into();
95 let value = value.into();
96 let size = ENTRY_OVERHEAD + name.len() + value.len();
97 Header { name, value, size }
98 }
99
100 #[inline]
103 pub(crate) fn size(&self) -> usize {
104 self.size
105 }
106
107 #[inline]
108 pub fn name(&self) -> &[u8] {
109 &self.name
110 }
111
112 #[inline]
113 pub fn value(&self) -> &[u8] {
114 &self.value
115 }
116}
117
118#[derive(Debug)]
121pub(crate) struct Table {
122 entries: VecDeque<Header>,
125 size: usize,
127 max_size: usize,
129}
130
131impl Table {
132 #[inline]
133 pub(crate) fn new() -> Self {
134 Table::with_max_size(DEFAULT_MAX_SIZE)
135 }
136
137 #[inline]
140 pub(crate) fn with_max_size(max_size: usize) -> Self {
141 Table {
142 entries: VecDeque::new(),
143 size: 0,
144 max_size,
145 }
146 }
147
148 #[inline]
151 pub(crate) fn get(&self, index: usize) -> Option<Header> {
152 if index == 0 {
153 return None;
154 }
155 if index <= STATIC_LEN {
156 let (name, value) = STATIC_TABLE[index - 1];
157 return Some(Header::new(
159 Bytes::from_static(name),
160 Bytes::from_static(value),
161 ));
162 }
163 self.entries.get(index - STATIC_LEN - 1).cloned()
164 }
165
166 #[cfg(test)]
168 #[inline]
169 pub(crate) fn dynamic_len(&self) -> usize {
170 self.entries.len()
171 }
172
173 #[inline]
177 pub(crate) fn find(&self, name: &[u8], value: &[u8]) -> Option<usize> {
178 for (i, (n, v)) in STATIC_TABLE.iter().enumerate() {
179 if *n == name && *v == value {
180 return Some(i + 1);
181 }
182 }
183 for (i, entry) in self.entries.iter().enumerate() {
184 if entry.name() == name && entry.value() == value {
185 return Some(STATIC_LEN + i + 1);
186 }
187 }
188 None
189 }
190
191 #[inline]
195 pub(crate) fn find_name(&self, name: &[u8]) -> Option<usize> {
196 for (i, (n, _)) in STATIC_TABLE.iter().enumerate() {
197 if *n == name {
198 return Some(i + 1);
199 }
200 }
201 for (i, entry) in self.entries.iter().enumerate() {
202 if entry.name() == name {
203 return Some(STATIC_LEN + i + 1);
204 }
205 }
206 None
207 }
208
209 #[cfg(test)]
211 #[inline]
212 pub(crate) fn len(&self) -> usize {
213 STATIC_LEN + self.entries.len()
214 }
215
216 #[cfg(test)]
218 #[inline]
219 pub(crate) fn size(&self) -> usize {
220 self.size
221 }
222
223 #[cfg(test)]
224 #[inline]
225 pub(crate) fn max_size(&self) -> usize {
226 self.max_size
227 }
228
229 #[inline]
233 pub(crate) fn set_max_size(&mut self, max_size: usize) {
234 self.max_size = max_size;
235 while self.size > self.max_size {
236 match self.entries.pop_back() {
237 Some(entry) => self.size -= entry.size(),
238 None => break,
239 }
240 }
241 }
242
243 #[inline]
247 pub(crate) fn add(&mut self, header: Header) {
248 if header.size() > self.max_size {
249 self.entries.clear();
250 self.size = 0;
251 return;
252 }
253 while self.size + header.size() > self.max_size {
254 match self.entries.pop_back() {
255 Some(entry) => self.size -= entry.size(),
256 None => break,
257 }
258 }
259 self.size += header.size();
260 self.entries.push_front(header);
261 }
262}
263
264impl Default for Table {
265 #[inline]
266 fn default() -> Self {
267 Self::new()
268 }
269}
270
271const DEFAULT_MAX_SIZE: usize = 4096;
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278
279 fn header(name: &str, value: &str) -> Header {
280 Header::new(
281 Bytes::copy_from_slice(name.as_bytes()),
282 Bytes::copy_from_slice(value.as_bytes()),
283 )
284 }
285
286 #[test]
287 fn static_table_contents() {
288 assert_eq!(
289 Table::new().get(1),
290 Some(Header::new(
291 Bytes::from_static(b":authority"),
292 Bytes::from_static(b"")
293 ))
294 );
295 assert_eq!(
296 Table::new().get(2),
297 Some(Header::new(
298 Bytes::from_static(b":method"),
299 Bytes::from_static(b"GET")
300 ))
301 );
302 assert_eq!(
303 Table::new().get(16),
304 Some(Header::new(
305 Bytes::from_static(b"accept-encoding"),
306 Bytes::from_static(b"gzip, deflate")
307 ))
308 );
309 assert_eq!(
310 Table::new().get(61),
311 Some(Header::new(
312 Bytes::from_static(b"www-authenticate"),
313 Bytes::from_static(b"")
314 ))
315 );
316 assert_eq!(Table::new().get(0), None);
317 }
318
319 #[test]
320 fn entry_size_math() {
321 assert_eq!(header("test", "abc").size(), 39);
323 assert_eq!(header("", "").size(), 32);
324 }
325
326 #[test]
327 fn add_and_fetch_order() {
328 let mut table = Table::with_max_size(200);
329 table.add(header("a", "1"));
330 table.add(header("b", "2"));
331 table.add(header("c", "3"));
332
333 assert_eq!(table.get(62), Some(header("c", "3")));
335 assert_eq!(table.get(63), Some(header("b", "2")));
336 assert_eq!(table.get(64), Some(header("a", "1")));
337 assert_eq!(table.get(65), None);
338 assert_eq!(table.dynamic_len(), 3);
339 assert_eq!(table.len(), STATIC_LEN + 3);
340 assert_eq!(table.size(), 34 * 3);
341 }
342
343 #[test]
344 fn eviction_from_end() {
345 let mut table = Table::with_max_size(70);
347 table.add(header("a", "1"));
348 table.add(header("b", "2"));
349 table.add(header("c", "3"));
350
351 assert_eq!(table.dynamic_len(), 2);
352 assert_eq!(table.get(62), Some(header("c", "3")));
353 assert_eq!(table.get(63), Some(header("b", "2")));
354 assert_eq!(table.get(64), None);
355 assert_eq!(table.size(), 68);
356 }
357
358 #[test]
359 fn entry_larger_than_max_empties_table() {
360 let mut table = Table::with_max_size(100);
361 table.add(header("x", "1"));
362 table.add(header("y", "2"));
363 assert_eq!(table.dynamic_len(), 2);
364
365 let big = header(&"v".repeat(60), &"v".repeat(9));
367 assert_eq!(big.size(), 101);
368 table.add(big);
369
370 assert_eq!(table.dynamic_len(), 0);
371 assert_eq!(table.size(), 0);
372 }
373
374 #[test]
375 fn entry_exactly_max_size_is_added() {
376 let mut table = Table::with_max_size(101);
377 let big = header(&"v".repeat(60), &"v".repeat(9));
378 assert_eq!(big.size(), 101);
379 table.add(big);
380 assert_eq!(table.dynamic_len(), 1);
381 }
382
383 #[test]
384 fn size_update_evicts() {
385 let mut table = Table::with_max_size(200);
386 table.add(header("a", "1"));
387 table.add(header("b", "2"));
388 table.add(header("c", "3"));
389
390 table.set_max_size(70);
391 assert_eq!(table.dynamic_len(), 2);
392 assert_eq!(table.get(62), Some(header("c", "3")));
393 assert_eq!(table.size(), 68);
394
395 table.set_max_size(0);
396 assert_eq!(table.dynamic_len(), 0);
397 assert_eq!(table.size(), 0);
398
399 table.set_max_size(200);
402 assert_eq!(table.dynamic_len(), 0);
403 table.add(header("d", "4"));
404 assert_eq!(table.get(62), Some(header("d", "4")));
405 }
406
407 #[test]
408 fn max_size_tracks_requests() {
409 let mut table = Table::with_max_size(128);
410 assert_eq!(table.max_size(), 128);
411 table.set_max_size(256);
412 assert_eq!(table.max_size(), 256);
413 }
414
415 #[test]
416 fn default_max_size() {
417 assert_eq!(Table::new().max_size(), DEFAULT_MAX_SIZE);
418 assert_eq!(Table::default().max_size(), DEFAULT_MAX_SIZE);
419 }
420
421 #[test]
422 fn grows_and_reuses_slots() {
423 let mut table = Table::with_max_size(200);
426 for i in 0..100 {
427 let entry = header(&format!("k{i}"), "v");
428 table.add(entry.clone());
429 assert!(table.size() <= table.max_size(), "iteration {i}");
430 assert_eq!(table.get(62), Some(entry), "iteration {i}");
431 }
432 assert_eq!(table.get(62), Some(header("k99", "v")));
433 assert_eq!(
434 table.size(),
435 table.dynamic_len() * table.get(62).unwrap().size()
436 );
437 }
438}