micro_web/date/mod.rs
1//! HTTP date header value management service.
2//!
3//! This module provides a service for efficiently managing and updating HTTP date header values
4//! in a concurrent environment. It updates the date string periodically to avoid repeated
5//! date string formatting operations in high-concurrency scenarios.
6
7use arc_swap::ArcSwap;
8use http::HeaderValue;
9use httpdate::fmt_http_date;
10use std::sync::Arc;
11use std::time::{Duration, SystemTime};
12
13mod date_service_decorator;
14
15pub use date_service_decorator::DateServiceDecorator;
16
17/// A service that maintains and periodically updates the current HTTP date string.
18///
19/// This service runs a background task that updates the date string every 700ms,
20/// providing an efficient way to access formatted HTTP date strings without
21/// formatting them on every request.
22pub struct DateService {
23 current: Arc<ArcSwap<(SystemTime, HeaderValue)>>,
24 handle: tokio::task::JoinHandle<()>,
25}
26
27impl DateService {
28 /// Creates a new `DateService` instance.
29 ///
30 /// This method initializes the service with the current system time and starts
31 /// a background task that updates the date string every 700ms.
32 ///
33 /// # Returns
34 /// Returns a new `DateService` instance with the background update task running.
35 pub(crate) fn new() -> Self {
36 let system_time = SystemTime::now();
37 let http_date = fmt_http_date(system_time);
38 let date_value = HeaderValue::try_from(http_date).expect("http_date should not fail");
39
40 let current = Arc::new(ArcSwap::from_pointee((system_time, date_value)));
41 let current_arc = Arc::clone(¤t);
42
43 let handle = tokio::spawn(async move {
44 loop {
45 tokio::time::sleep(Duration::from_millis(700)).await;
46 let system_time = SystemTime::now();
47 let http_date = fmt_http_date(system_time);
48 let date_value = HeaderValue::try_from(http_date).expect("http_date should not fail");
49 current_arc.store(Arc::new((system_time, date_value)));
50 }
51 });
52
53 DateService { current, handle }
54 }
55
56 /// Provides access to the current HTTP date string through a callback function.
57 ///
58 /// This method allows safe access to the current date string without exposing
59 /// the internal synchronization mechanisms.
60 pub(crate) fn with_http_date<F>(&self, mut f: F)
61 where
62 F: FnMut(&HeaderValue),
63 {
64 let date = &self.current.load().1;
65 f(date)
66 }
67}
68
69/// Implements the `Drop` trait to ensure the background task is properly cleaned up
70/// when the `DateService` is dropped.
71impl Drop for DateService {
72 fn drop(&mut self) {
73 self.handle.abort();
74 }
75}