surf_middleware_cache/managers/
cacache.rs1use 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#[derive(Debug, Clone)]
13pub struct CACacheManager {
14 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 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 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}