surf_middleware_cache/managers/
cacache.rs

1use std::collections::HashMap;
2use std::convert::TryInto;
3
4use crate::{CacheManager, Result};
5
6use http_cache_semantics::CachePolicy;
7use http_types::Version;
8use serde::{Deserialize, Serialize};
9use surf::{Request, Response};
10
11/// Implements [`CacheManager`] with [`cacache`](https://github.com/zkat/cacache-rs) as the backend.
12#[derive(Debug, Clone)]
13pub struct CACacheManager {
14    /// Directory where the cache will be stored.
15    pub path: String,
16}
17
18impl Default for CACacheManager {
19    fn default() -> Self {
20        CACacheManager {
21            path: "./surf-cacache".into(),
22        }
23    }
24}
25
26#[derive(Debug, Deserialize, Serialize)]
27struct Store {
28    response: StoredResponse,
29    policy: CachePolicy,
30}
31
32#[derive(Debug, Deserialize, Serialize)]
33struct StoredResponse {
34    body: Vec<u8>,
35    headers: HashMap<String, String>,
36    status: u16,
37    version: Version,
38}
39
40async fn to_store(res: &mut Response, policy: CachePolicy) -> Result<Store> {
41    let mut headers = HashMap::new();
42    for header in res.iter() {
43        headers.insert(header.0.as_str().to_owned(), header.1.as_str().to_owned());
44    }
45    let status = res.status().into();
46    let version = res.version().unwrap_or(Version::Http1_1);
47    let body: Vec<u8> = res.body_bytes().await?;
48    Ok(Store {
49        response: StoredResponse {
50            body,
51            headers,
52            status,
53            version,
54        },
55        policy,
56    })
57}
58
59fn from_store(store: &Store) -> Result<Response> {
60    let mut res = http_types::Response::new(http_types::StatusCode::Ok);
61    for header in &store.response.headers {
62        let val =
63            http_types::headers::HeaderValue::from_bytes(header.1.as_bytes().to_vec()).unwrap();
64        res.insert_header(header.0.as_str(), val);
65    }
66    res.set_status(store.response.status.try_into()?);
67    res.set_version(Some(store.response.version));
68    res.set_body(store.response.body.clone());
69    Ok(Response::from(res))
70}
71
72fn req_key(req: &Request) -> String {
73    format!("{}:{}", req.method(), req.url())
74}
75
76#[allow(dead_code)]
77impl CACacheManager {
78    /// Clears out the entire cache.
79    pub async fn clear(&self) -> Result<()> {
80        cacache::clear(&self.path).await?;
81        Ok(())
82    }
83}
84
85#[surf::utils::async_trait]
86impl CacheManager for CACacheManager {
87    async fn get(&self, req: &Request) -> Result<Option<(Response, CachePolicy)>> {
88        let store: Store = match cacache::read(&self.path, &req_key(req)).await {
89            Ok(d) => bincode::deserialize(&d)?,
90            Err(_e) => {
91                return Ok(None);
92            }
93        };
94        Ok(Some((from_store(&store)?, store.policy)))
95    }
96
97    // TODO - This needs some reviewing.
98    async fn put(
99        &self,
100        req: &Request,
101        res: &mut Response,
102        policy: CachePolicy,
103    ) -> Result<Response> {
104        let data = to_store(res, policy).await?;
105        let bytes = bincode::serialize(&data).unwrap();
106        cacache::write(&self.path, &req_key(req), bytes).await?;
107        let mut ret_res = http_types::Response::new(res.status());
108        ret_res.set_body(data.response.body);
109        for header in res.iter() {
110            ret_res.insert_header(header.0, header.1);
111        }
112        ret_res.set_status(res.status());
113        ret_res.set_version(res.version());
114        Ok(Response::from(ret_res))
115    }
116
117    async fn delete(&self, req: &Request) -> Result<()> {
118        Ok(cacache::remove(&self.path, &req_key(req)).await?)
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125    use crate::{get_request_parts, get_response_parts};
126    use http_types::{Method, Response, StatusCode};
127    use std::str::FromStr;
128    use surf::{Request, Result};
129
130    #[async_std::test]
131    async fn can_cache_response() -> Result<()> {
132        let url = surf::http::Url::from_str("https://example.com")?;
133        let mut res = Response::new(StatusCode::Ok);
134        res.set_body("test");
135        let mut res = surf::Response::from(res);
136        let req = Request::new(Method::Get, url);
137        let policy = CachePolicy::new(&get_request_parts(&req)?, &get_response_parts(&res)?);
138        let manager = CACacheManager::default();
139        manager.put(&req, &mut res, policy).await?;
140        let data = manager.get(&req).await?;
141        let body = match data {
142            Some(mut d) => d.0.body_string().await?,
143            None => String::new(),
144        };
145        assert_eq!(&body, "test");
146        manager.delete(&req).await?;
147        let data = manager.get(&req).await?;
148        assert!(data.is_none());
149        manager.clear().await?;
150        Ok(())
151    }
152}