1use core::{
4 cell::RefCell,
5 fmt::{self, Write},
6 ops::Deref,
7 time::Duration,
8};
9
10use std::{rc::Rc, time::SystemTime};
11
12use tokio::{
13 task::JoinHandle,
14 time::{Instant, interval},
15};
16
17use crate::http::header::HeaderValue;
18
19const DATE_VALUE_LENGTH: usize = 29;
21
22const DAY: [&[u8; 3]; 7] = [b"Sun", b"Mon", b"Tue", b"Wed", b"Thu", b"Fri", b"Sat"];
23
24const MONTH: [&[u8; 3]; 12] = [
25 b"Jan", b"Feb", b"Mar", b"Apr", b"May", b"Jun", b"Jul", b"Aug", b"Sep", b"Oct", b"Nov", b"Dec",
26];
27
28const TEMPLATE: [u8; DATE_VALUE_LENGTH] = *b"Xxx, 00 Xxx 0000 00:00:00 GMT";
31
32const fn two_digits<const OFF: usize>(buf: &mut [u8; DATE_VALUE_LENGTH], n: u32) {
37 const {
38 assert!(
39 OFF + 1 < DATE_VALUE_LENGTH,
40 "two_digits would write past the date buffer"
41 )
42 }
43 buf[OFF] = b'0' + (n / 10) as u8;
44 buf[OFF + 1] = b'0' + (n % 10) as u8;
45}
46
47struct HttpDate {
54 secs: i64,
56}
57
58impl From<SystemTime> for HttpDate {
59 fn from(time: SystemTime) -> Self {
60 let secs = match time.duration_since(SystemTime::UNIX_EPOCH) {
61 Ok(dur) => dur.as_secs() as i64,
62 Err(e) => -(e.duration().as_secs() as i64),
63 };
64 Self { secs }
65 }
66}
67
68impl fmt::Display for HttpDate {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 let days = self.secs.div_euclid(86400);
73 let rem = self.secs.rem_euclid(86400);
74
75 let z = days + 719468;
78 let era = z.div_euclid(146097);
79 let doe = z - era * 146097;
80 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
81 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
82 let mp = (5 * doy + 2) / 153;
83 let day = (doy - (153 * mp + 2) / 5 + 1) as u32;
84 let month = (if mp < 10 { mp + 3 } else { mp - 9 }) as u32;
85 let year = yoe + era * 400 + if month <= 2 { 1 } else { 0 };
86
87 let week_day = DAY[(days + 4).rem_euclid(7) as usize];
89
90 let mut buf = TEMPLATE;
91
92 buf[..3].copy_from_slice(week_day);
93 two_digits::<5>(&mut buf, day);
94 buf[8..11].copy_from_slice(MONTH[month as usize - 1]);
95
96 let year = year.clamp(0, 9999) as u32;
99 two_digits::<12>(&mut buf, year / 100);
100 two_digits::<14>(&mut buf, year % 100);
101 two_digits::<17>(&mut buf, (rem / 3600) as u32);
102 two_digits::<20>(&mut buf, (rem % 3600 / 60) as u32);
103 two_digits::<23>(&mut buf, (rem % 60) as u32);
104
105 f.write_str(core::str::from_utf8(&buf).expect("date buffer is always ASCII"))
108 }
109}
110
111pub trait DateTime {
115 const DATE_SIZE_HINT: usize = DATE_VALUE_LENGTH;
117
118 fn with_date<F, O>(&self, f: F) -> O
120 where
121 F: FnOnce(&[u8]) -> O;
122
123 fn with_date_header<F, O>(&self, f: F) -> O
124 where
125 F: FnOnce(&HeaderValue) -> O;
126
127 fn now(&self) -> Instant;
128}
129
130pub struct DateTimeService {
132 state: Rc<RefCell<DateTimeState>>,
133 handle: JoinHandle<()>,
134}
135
136impl Drop for DateTimeService {
137 fn drop(&mut self) {
138 self.handle.abort();
140 }
141}
142
143impl Default for DateTimeService {
144 fn default() -> Self {
145 Self::new()
146 }
147}
148
149impl DateTimeService {
150 pub fn new() -> Self {
151 let state = Rc::new(RefCell::new(DateTimeState::default()));
153 let state_clone = Rc::clone(&state);
154 let handle = tokio::task::spawn_local(async move {
157 let mut interval = interval(Duration::from_millis(500));
158 loop {
159 let _ = interval.tick().await;
160 *state_clone.borrow_mut() = DateTimeState::default();
161 }
162 });
163
164 Self { state, handle }
165 }
166
167 #[inline]
168 pub fn get(&self) -> &DateTimeHandle {
169 self.state.deref()
170 }
171}
172
173pub(crate) type DateTimeHandle = RefCell<DateTimeState>;
174
175#[derive(Clone)]
177pub struct DateTimeState {
178 pub date: [u8; DATE_VALUE_LENGTH],
179 pub date_header: HeaderValue,
180 pub now: Instant,
181}
182
183impl Default for DateTimeState {
184 fn default() -> Self {
185 let mut date = Self {
186 date: [0; DATE_VALUE_LENGTH],
187 date_header: HeaderValue::from_static(""),
188 now: Instant::now(),
189 };
190 let _ = write!(date, "{}", HttpDate::from(SystemTime::now()));
191 date.date_header = HeaderValue::from_bytes(&date.date).unwrap();
192 date
193 }
194}
195
196impl Write for DateTimeState {
197 fn write_str(&mut self, s: &str) -> fmt::Result {
198 self.date[..].copy_from_slice(s.as_bytes());
199 Ok(())
200 }
201}
202
203impl DateTime for DateTimeHandle {
204 #[inline]
206 fn with_date<F, O>(&self, f: F) -> O
207 where
208 F: FnOnce(&[u8]) -> O,
209 {
210 let date = self.borrow();
211 f(&date.date[..])
212 }
213
214 #[inline]
215 fn with_date_header<F, O>(&self, f: F) -> O
216 where
217 F: FnOnce(&HeaderValue) -> O,
218 {
219 let date = self.borrow();
220 f(&date.date_header)
221 }
222
223 #[inline(always)]
224 fn now(&self) -> Instant {
225 self.borrow().now
226 }
227}
228
229pub struct SystemTimeDateTimeHandler;
231
232impl DateTime for SystemTimeDateTimeHandler {
233 #[allow(dead_code)]
235 fn with_date<F, O>(&self, f: F) -> O
236 where
237 F: FnOnce(&[u8]) -> O,
238 {
239 let date = HttpDate::from(SystemTime::now()).to_string();
240 f(date.as_bytes())
241 }
242
243 #[allow(dead_code)]
244 fn with_date_header<F, O>(&self, f: F) -> O
245 where
246 F: FnOnce(&HeaderValue) -> O,
247 {
248 self.with_date(|date| {
249 let val = HeaderValue::from_bytes(date).unwrap();
250 f(&val)
251 })
252 }
253
254 fn now(&self) -> Instant {
255 Instant::now()
256 }
257}
258
259#[cfg(test)]
260mod test {
261 use super::*;
262
263 #[test]
264 fn imf_fixdate_vectors() {
265 let format = |secs: i64| HttpDate { secs }.to_string();
266
267 assert_eq!(format(784_111_777), "Sun, 06 Nov 1994 08:49:37 GMT");
269 assert_eq!(format(0), "Thu, 01 Jan 1970 00:00:00 GMT");
270 assert_eq!(format(2_147_483_647), "Tue, 19 Jan 2038 03:14:07 GMT");
271 assert_eq!(format(4_102_444_800), "Fri, 01 Jan 2100 00:00:00 GMT");
272 assert_eq!(format(951_782_400), "Tue, 29 Feb 2000 00:00:00 GMT");
274 assert_eq!(format(-1), "Wed, 31 Dec 1969 23:59:59 GMT");
275 }
276
277 #[test]
280 fn formatted_length_is_always_the_date_value_length() {
281 let steps = (0..100_000).map(|i| i * 40_000).chain(0..86_400);
283 for secs in steps {
284 let date = HttpDate { secs }.to_string();
285 assert_eq!(date.len(), DATE_VALUE_LENGTH, "{date}");
286 }
287 }
288
289 #[test]
290 fn date_time_state_default_is_a_valid_header() {
291 let state = DateTimeState::default();
292 assert_eq!(state.date.len(), DATE_VALUE_LENGTH);
293 assert_eq!(state.date_header.as_bytes(), &state.date[..]);
294 assert!(state.date.ends_with(b" GMT"));
295 }
296}