xitca_http/
date.rs

1//! low resolution async date time for reduced syscall for generating http date time.
2
3use std::{
4    cell::RefCell,
5    fmt::{self, Write},
6    ops::Deref,
7    rc::Rc,
8    time::{Duration, SystemTime},
9};
10
11use httpdate::HttpDate;
12use tokio::{
13    task::JoinHandle,
14    time::{Instant, interval},
15};
16
17// The length of byte representation of HttpDate
18const DATE_VALUE_LENGTH: usize = 29;
19
20/// Trait for getting current date/time.
21///
22/// This is usually used by a low resolution of timer to reduce frequent syscall to OS.
23pub trait DateTime {
24    /// The size hint of slice by Self::date method.
25    const DATE_SIZE_HINT: usize = DATE_VALUE_LENGTH;
26
27    /// closure would receive byte slice representation of [HttpDate].
28    fn with_date<F, O>(&self, f: F) -> O
29    where
30        F: FnOnce(&[u8]) -> O;
31
32    fn now(&self) -> Instant;
33}
34
35/// Struct with Date update periodically at 500 milliseconds interval.
36pub struct DateTimeService {
37    state: Rc<RefCell<DateTimeState>>,
38    handle: JoinHandle<()>,
39}
40
41impl Drop for DateTimeService {
42    fn drop(&mut self) {
43        // stop the timer update async task on drop.
44        self.handle.abort();
45    }
46}
47
48impl Default for DateTimeService {
49    fn default() -> Self {
50        Self::new()
51    }
52}
53
54impl DateTimeService {
55    pub fn new() -> Self {
56        // shared date and timer for Date and update async task.
57        let state = Rc::new(RefCell::new(DateTimeState::default()));
58        let state_clone = Rc::clone(&state);
59        // spawn an async task sleep for 1 sec and update date in a loop.
60        // handle is used to stop the task on Date drop.
61        let handle = tokio::task::spawn_local(async move {
62            let mut interval = interval(Duration::from_millis(500));
63            loop {
64                let _ = interval.tick().await;
65                *state_clone.borrow_mut() = DateTimeState::default();
66            }
67        });
68
69        Self { state, handle }
70    }
71
72    #[inline]
73    pub fn get(&self) -> &DateTimeHandle {
74        self.state.deref()
75    }
76}
77
78pub(crate) type DateTimeHandle = RefCell<DateTimeState>;
79
80/// struct contains byte representation of [HttpDate] and [Instant].
81#[derive(Copy, Clone)]
82pub struct DateTimeState {
83    pub date: [u8; DATE_VALUE_LENGTH],
84    pub now: Instant,
85}
86
87impl Default for DateTimeState {
88    fn default() -> Self {
89        let mut date = Self {
90            date: [0; DATE_VALUE_LENGTH],
91            now: Instant::now(),
92        };
93        let _ = write!(date, "{}", HttpDate::from(SystemTime::now()));
94        date
95    }
96}
97
98impl Write for DateTimeState {
99    fn write_str(&mut self, s: &str) -> fmt::Result {
100        self.date[..].copy_from_slice(s.as_bytes());
101        Ok(())
102    }
103}
104
105impl DateTime for DateTimeHandle {
106    // TODO: remove this allow
107    #[allow(dead_code)]
108    #[inline]
109    fn with_date<F, O>(&self, f: F) -> O
110    where
111        F: FnOnce(&[u8]) -> O,
112    {
113        let date = self.borrow();
114        f(&date.date[..])
115    }
116
117    #[inline(always)]
118    fn now(&self) -> Instant {
119        self.borrow().now
120    }
121}
122
123/// Time handler powered by plain OS system time. useful for testing purpose.
124pub struct SystemTimeDateTimeHandler;
125
126impl DateTime for SystemTimeDateTimeHandler {
127    // TODO: remove this allow
128    #[allow(dead_code)]
129    fn with_date<F, O>(&self, f: F) -> O
130    where
131        F: FnOnce(&[u8]) -> O,
132    {
133        let date = HttpDate::from(SystemTime::now()).to_string();
134        f(date.as_bytes())
135    }
136
137    fn now(&self) -> Instant {
138        Instant::now()
139    }
140}