orinium_browser/platform/network/
cache.rs

1//! HTTP response caching with TTL support.
2
3use std::collections::HashMap;
4use std::sync::{Arc, RwLock};
5use std::time::{Duration, SystemTime};
6use url::Url;
7
8#[allow(dead_code)]
9#[derive(Debug, Clone)]
10pub struct CachedResponse {
11    pub body: Vec<u8>,
12    pub headers: Vec<(String, String)>,
13    pub cached_at: SystemTime,
14    pub expires_at: Option<SystemTime>,
15}
16
17#[derive(Debug, Clone)]
18pub struct Cache {
19    store: Arc<RwLock<HashMap<String, CachedResponse>>>,
20}
21
22#[allow(dead_code)]
23impl Default for Cache {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29impl Cache {
30    pub fn new() -> Self {
31        Self {
32            store: Arc::new(RwLock::new(HashMap::new())),
33        }
34    }
35
36    pub fn get(&self, url: &Url) -> Option<CachedResponse> {
37        let store = self.store.read().ok()?;
38        let key = url.as_str();
39
40        if let Some(entry) = store.get(key) {
41            if let Some(exp) = entry.expires_at
42                && SystemTime::now() > exp
43            {
44                return None;
45            }
46            return Some(entry.clone());
47        }
48        None
49    }
50
51    pub fn set(&self, url: &Url, body: Vec<u8>, headers: Vec<(String, String)>) {
52        let mut store = self.store.write().expect("RwLock poisoned");
53        let key = url.as_str().to_string();
54
55        let mut expires = None;
56        if let Some((_, cc)) = headers
57            .iter()
58            .find(|(n, _)| n.eq_ignore_ascii_case("cache-control"))
59            && let Some(pos) = cc.find("max-age=")
60            && let Ok(max_age) = cc[pos + 8..]
61                .split(|c: char| !c.is_ascii_digit())
62                .next()
63                .unwrap_or("0")
64                .parse::<u64>()
65        {
66            expires = Some(SystemTime::now() + Duration::from_secs(max_age));
67        }
68
69        store.insert(
70            key,
71            CachedResponse {
72                body,
73                headers,
74                cached_at: SystemTime::now(),
75                expires_at: expires,
76            },
77        );
78    }
79
80    pub fn clear(&self) {
81        let mut store = self.store.write().expect("RwLock poisoned");
82        store.clear();
83    }
84}