1 use std::cell::RefCell;
2 use std::fmt::{self, Write};
3 use std::str;
4 use std::time::{Duration, SystemTime};
5 
6 #[cfg(feature = "http2")]
7 use http::header::HeaderValue;
8 use httpdate::HttpDate;
9 
10 // "Sun, 06 Nov 1994 08:49:37 GMT".len()
11 pub(crate) const DATE_VALUE_LENGTH: usize = 29;
12 
13 #[cfg(feature = "http1")]
extend(dst: &mut Vec<u8>)14 pub(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")]
update()21 pub(crate) fn update() {
22     CACHED.with(|cache| {
23         cache.borrow_mut().check();
24     })
25 }
26 
27 #[cfg(feature = "http2")]
update_and_header_value() -> HeaderValue28 pub(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 
36 struct CachedDate {
37     bytes: [u8; DATE_VALUE_LENGTH],
38     pos: usize,
39     next_update: SystemTime,
40 }
41 
42 thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate::new()));
43 
44 impl CachedDate {
new() -> Self45     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 
buffer(&self) -> &[u8]55     fn buffer(&self) -> &[u8] {
56         &self.bytes[..]
57     }
58 
check(&mut self)59     fn check(&mut self) {
60         let now = SystemTime::now();
61         if now > self.next_update {
62             self.update(now);
63         }
64     }
65 
update(&mut self, now: SystemTime)66     fn update(&mut self, now: SystemTime) {
67         self.render(now);
68         self.next_update = now + Duration::new(1, 0);
69     }
70 
render(&mut self, now: SystemTime)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 
78 impl fmt::Write for CachedDate {
write_str(&mut self, s: &str) -> fmt::Result79     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)]
88 mod tests {
89     use super::*;
90 
91     #[cfg(feature = "nightly")]
92     use test::Bencher;
93 
94     #[test]
test_date_len()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]
bench_date_check(b: &mut Bencher)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]
bench_date_render(b: &mut Bencher)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 }
125