use std::cell::RefCell; use std::fmt::{self, Write}; use std::str; use std::time::{Duration, SystemTime}; #[cfg(feature = "http2")] use http::header::HeaderValue; use httpdate::HttpDate; // "Sun, 06 Nov 1994 08:49:37 GMT".len() pub(crate) const DATE_VALUE_LENGTH: usize = 29; #[cfg(feature = "http1")] pub(crate) fn extend(dst: &mut Vec) { CACHED.with(|cache| { dst.extend_from_slice(cache.borrow().buffer()); }) } #[cfg(feature = "http1")] pub(crate) fn update() { CACHED.with(|cache| { cache.borrow_mut().check(); }) } #[cfg(feature = "http2")] pub(crate) fn update_and_header_value() -> HeaderValue { CACHED.with(|cache| { let mut cache = cache.borrow_mut(); cache.check(); HeaderValue::from_bytes(cache.buffer()).expect("Date format should be valid HeaderValue") }) } struct CachedDate { bytes: [u8; DATE_VALUE_LENGTH], pos: usize, next_update: SystemTime, } thread_local!(static CACHED: RefCell = RefCell::new(CachedDate::new())); impl CachedDate { fn new() -> Self { let mut cache = CachedDate { bytes: [0; DATE_VALUE_LENGTH], pos: 0, next_update: SystemTime::now(), }; cache.update(cache.next_update); cache } fn buffer(&self) -> &[u8] { &self.bytes[..] } fn check(&mut self) { let now = SystemTime::now(); if now > self.next_update { self.update(now); } } fn update(&mut self, now: SystemTime) { self.render(now); self.next_update = now + Duration::new(1, 0); } fn render(&mut self, now: SystemTime) { self.pos = 0; let _ = write!(self, "{}", HttpDate::from(now)); debug_assert!(self.pos == DATE_VALUE_LENGTH); } } impl fmt::Write for CachedDate { fn write_str(&mut self, s: &str) -> fmt::Result { let len = s.len(); self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes()); self.pos += len; Ok(()) } } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "nightly")] use test::Bencher; #[test] fn test_date_len() { assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len()); } #[cfg(feature = "nightly")] #[bench] fn bench_date_check(b: &mut Bencher) { let mut date = CachedDate::new(); // cache the first update date.check(); b.iter(|| { date.check(); }); } #[cfg(feature = "nightly")] #[bench] fn bench_date_render(b: &mut Bencher) { let mut date = CachedDate::new(); let now = SystemTime::now(); date.render(now); b.bytes = date.buffer().len() as u64; b.iter(|| { date.render(now); test::black_box(&date); }); } }