Skip to main content

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