Skip to main content

pingora_core/protocols/http/
date.rs

1// Copyright 2026 Cloudflare, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use chrono::DateTime;
16use http::header::HeaderValue;
17use std::cell::RefCell;
18use std::time::{Duration, SystemTime};
19
20fn to_date_string(epoch_sec: i64) -> String {
21    let dt = DateTime::from_timestamp(epoch_sec, 0).unwrap();
22    dt.format("%a, %d %b %Y %H:%M:%S GMT").to_string()
23}
24
25struct CacheableDate {
26    h1_date: HeaderValue,
27    epoch: Duration,
28}
29
30impl CacheableDate {
31    pub fn new() -> Self {
32        let d = SystemTime::now()
33            .duration_since(SystemTime::UNIX_EPOCH)
34            .unwrap();
35        CacheableDate {
36            h1_date: HeaderValue::from_str(&to_date_string(d.as_secs() as i64)).unwrap(),
37            epoch: d,
38        }
39    }
40
41    pub fn update(&mut self, d_now: Duration) {
42        if d_now.as_secs() != self.epoch.as_secs() {
43            self.epoch = d_now;
44            self.h1_date = HeaderValue::from_str(&to_date_string(d_now.as_secs() as i64)).unwrap();
45        }
46    }
47
48    pub fn get_date(&mut self) -> HeaderValue {
49        let d = SystemTime::now()
50            .duration_since(SystemTime::UNIX_EPOCH)
51            .unwrap();
52        self.update(d);
53        self.h1_date.clone()
54    }
55}
56
57thread_local! {
58    static CACHED_DATE: RefCell<CacheableDate>
59        = RefCell::new(CacheableDate::new());
60}
61
62pub fn get_cached_date() -> HeaderValue {
63    CACHED_DATE.with(|cache_date| (*cache_date.borrow_mut()).get_date())
64}
65
66#[cfg(test)]
67mod test {
68    use super::*;
69
70    fn now_date_header() -> HeaderValue {
71        HeaderValue::from_str(&to_date_string(
72            SystemTime::now()
73                .duration_since(SystemTime::UNIX_EPOCH)
74                .unwrap()
75                .as_secs() as i64,
76        ))
77        .unwrap()
78    }
79
80    #[test]
81    fn test_date_string() {
82        let date_str = to_date_string(1);
83        assert_eq!("Thu, 01 Jan 1970 00:00:01 GMT", date_str);
84    }
85
86    #[test]
87    fn test_date_cached() {
88        assert_eq!(get_cached_date(), now_date_header());
89    }
90}