hyper/common/
date.rs

1use std::cell::RefCell;
2use std::fmt::{self, Write};
3use std::str;
4use std::time::{Duration, SystemTime};
5
6#[cfg(feature = "http2")]
7use http::header::HeaderValue;
8use httpdate::HttpDate;
9
10// "Sun, 06 Nov 1994 08:49:37 GMT".len()
11pub(crate) const DATE_VALUE_LENGTH: usize = 29;
12
13#[cfg(feature = "http1")]
14pub(crate) fn extend(dst: &mut Vec<u8>) {
15    CACHED.with(|cache| {
16        dst.extend_from_slice(cache.borrow().buffer());
17    })
18}
19
20#[cfg(feature = "http1")]
21pub(crate) fn update() {
22    CACHED.with(|cache| {
23        cache.borrow_mut().check();
24    })
25}
26
27#[cfg(feature = "http2")]
28pub(crate) fn update_and_header_value() -> HeaderValue {
29    CACHED.with(|cache| {
30        let mut cache = cache.borrow_mut();
31        cache.check();
32        HeaderValue::from_bytes(cache.buffer()).expect("Date format should be valid HeaderValue")
33    })
34}
35
36struct CachedDate {
37    bytes: [u8; DATE_VALUE_LENGTH],
38    pos: usize,
39    next_update: SystemTime,
40}
41
42thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate::new()));
43
44impl CachedDate {
45    fn new() -> Self {
46        let mut cache = CachedDate {
47            bytes: [0; DATE_VALUE_LENGTH],
48            pos: 0,
49            next_update: SystemTime::now(),
50        };
51        cache.update(cache.next_update);
52        cache
53    }
54
55    fn buffer(&self) -> &[u8] {
56        &self.bytes[..]
57    }
58
59    fn check(&mut self) {
60        let now = SystemTime::now();
61        if now > self.next_update {
62            self.update(now);
63        }
64    }
65
66    fn update(&mut self, now: SystemTime) {
67        self.render(now);
68        self.next_update = now + Duration::new(1, 0);
69    }
70
71    fn render(&mut self, now: SystemTime) {
72        self.pos = 0;
73        let _ = write!(self, "{}", HttpDate::from(now));
74        debug_assert!(self.pos == DATE_VALUE_LENGTH);
75    }
76}
77
78impl fmt::Write for CachedDate {
79    fn write_str(&mut self, s: &str) -> fmt::Result {
80        let len = s.len();
81        self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
82        self.pos += len;
83        Ok(())
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[cfg(feature = "nightly")]
92    use test::Bencher;
93
94    #[test]
95    fn test_date_len() {
96        assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
97    }
98
99    #[cfg(feature = "nightly")]
100    #[bench]
101    fn bench_date_check(b: &mut Bencher) {
102        let mut date = CachedDate::new();
103        // cache the first update
104        date.check();
105
106        b.iter(|| {
107            date.check();
108        });
109    }
110
111    #[cfg(feature = "nightly")]
112    #[bench]
113    fn bench_date_render(b: &mut Bencher) {
114        let mut date = CachedDate::new();
115        let now = SystemTime::now();
116        date.render(now);
117        b.bytes = date.buffer().len() as u64;
118
119        b.iter(|| {
120            date.render(now);
121            test::black_box(&date);
122        });
123    }
124}