hyper/common/
date.rs

1use std::cell::RefCell;
2use std::fmt::{self, Write};
3use std::str;
4use std::time::{Duration, SystemTime, UNIX_EPOCH};
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        cache.header_value.clone()
33    })
34}
35
36struct CachedDate {
37    bytes: [u8; DATE_VALUE_LENGTH],
38    pos: usize,
39    #[cfg(feature = "http2")]
40    header_value: HeaderValue,
41    next_update: SystemTime,
42}
43
44thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate::new()));
45
46impl CachedDate {
47    fn new() -> Self {
48        let mut cache = CachedDate {
49            bytes: [0; DATE_VALUE_LENGTH],
50            pos: 0,
51            #[cfg(feature = "http2")]
52            header_value: HeaderValue::from_static(""),
53            next_update: SystemTime::now(),
54        };
55        cache.update(cache.next_update);
56        cache
57    }
58
59    fn buffer(&self) -> &[u8] {
60        &self.bytes[..]
61    }
62
63    fn check(&mut self) {
64        let now = SystemTime::now();
65        if now > self.next_update {
66            self.update(now);
67        }
68    }
69
70    fn update(&mut self, now: SystemTime) {
71        let nanos = now
72            .duration_since(UNIX_EPOCH)
73            .unwrap_or_default()
74            .subsec_nanos();
75
76        self.render(now);
77        self.next_update = now + Duration::new(1, 0) - Duration::from_nanos(nanos as u64);
78    }
79
80    fn render(&mut self, now: SystemTime) {
81        self.pos = 0;
82        let _ = write!(self, "{}", HttpDate::from(now));
83        debug_assert!(self.pos == DATE_VALUE_LENGTH);
84        self.render_http2();
85    }
86
87    #[cfg(feature = "http2")]
88    fn render_http2(&mut self) {
89        self.header_value = HeaderValue::from_bytes(self.buffer())
90            .expect("Date format should be valid HeaderValue");
91    }
92
93    #[cfg(not(feature = "http2"))]
94    fn render_http2(&mut self) {}
95}
96
97impl fmt::Write for CachedDate {
98    fn write_str(&mut self, s: &str) -> fmt::Result {
99        let len = s.len();
100        self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
101        self.pos += len;
102        Ok(())
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109
110    #[cfg(feature = "nightly")]
111    use test::Bencher;
112
113    #[test]
114    fn test_date_len() {
115        assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
116    }
117
118    #[cfg(feature = "nightly")]
119    #[bench]
120    fn bench_date_check(b: &mut Bencher) {
121        let mut date = CachedDate::new();
122        // cache the first update
123        date.check();
124
125        b.iter(|| {
126            date.check();
127        });
128    }
129
130    #[cfg(feature = "nightly")]
131    #[bench]
132    fn bench_date_render(b: &mut Bencher) {
133        let mut date = CachedDate::new();
134        let now = SystemTime::now();
135        date.render(now);
136        b.bytes = date.buffer().len() as u64;
137
138        b.iter(|| {
139            date.render(now);
140            test::black_box(&date);
141        });
142    }
143}