1#![forbid(unsafe_code, future_incompatible)]
2#![deny(
3 missing_docs,
4 missing_debug_implementations,
5 missing_copy_implementations,
6 nonstandard_style,
7 unused_qualifications,
8 unused_import_braces,
9 unused_extern_crates,
10 trivial_casts,
11 trivial_numeric_casts
12)]
13#![allow(clippy::doc_lazy_continuation)]
14#![cfg_attr(docsrs, feature(doc_cfg))]
15mod error;
34
35use anyhow::anyhow;
36use std::{
37 collections::HashMap, convert::TryInto, str::FromStr, time::SystemTime,
38};
39
40pub use http::request::Parts;
41use http::{header::CACHE_CONTROL, request};
42use http_cache::{
43 BadHeader, BoxError, HitOrMiss, Middleware, Result, XCACHE, XCACHELOOKUP,
44};
45use http_cache_semantics::CachePolicy;
46use http_types::{headers::HeaderValue, Method, Response, StatusCode, Version};
47use surf::{middleware::Next, Client, Request};
48use url::Url;
49
50pub use http_cache::{
51 CacheManager, CacheMode, CacheOptions, HttpCache, HttpCacheOptions,
52 HttpResponse,
53};
54
55#[cfg(feature = "manager-cacache")]
56#[cfg_attr(docsrs, doc(cfg(feature = "manager-cacache")))]
57pub use http_cache::CACacheManager;
58
59#[cfg(feature = "manager-moka")]
60#[cfg_attr(docsrs, doc(cfg(feature = "manager-moka")))]
61pub use http_cache::{MokaCache, MokaCacheBuilder, MokaManager};
62
63#[derive(Debug)]
65pub struct Cache<T: CacheManager>(pub HttpCache<T>);
66
67pub(crate) struct SurfMiddleware<'a> {
69 pub req: Request,
70 pub client: Client,
71 pub next: Next<'a>,
72}
73
74#[async_trait::async_trait]
75impl Middleware for SurfMiddleware<'_> {
76 fn is_method_get_head(&self) -> bool {
77 self.req.method() == Method::Get || self.req.method() == Method::Head
78 }
79 fn policy(&self, response: &HttpResponse) -> Result<CachePolicy> {
80 Ok(CachePolicy::new(&self.parts()?, &response.parts()?))
81 }
82 fn policy_with_options(
83 &self,
84 response: &HttpResponse,
85 options: CacheOptions,
86 ) -> Result<CachePolicy> {
87 Ok(CachePolicy::new_options(
88 &self.parts()?,
89 &response.parts()?,
90 SystemTime::now(),
91 options,
92 ))
93 }
94 fn update_headers(&mut self, parts: &Parts) -> Result<()> {
95 for header in parts.headers.iter() {
96 let value = match HeaderValue::from_str(header.1.to_str()?) {
97 Ok(v) => v,
98 Err(_e) => return Err(Box::new(BadHeader)),
99 };
100 self.req.set_header(header.0.as_str(), value);
101 }
102 Ok(())
103 }
104 fn force_no_cache(&mut self) -> Result<()> {
105 self.req.insert_header(CACHE_CONTROL.as_str(), "no-cache");
106 Ok(())
107 }
108 fn parts(&self) -> Result<Parts> {
109 let mut converted = request::Builder::new()
110 .method(self.req.method().as_ref())
111 .uri(self.req.url().as_str())
112 .body(())?;
113 {
114 let headers = converted.headers_mut();
115 for header in self.req.iter() {
116 headers.insert(
117 http::header::HeaderName::from_str(header.0.as_str())?,
118 http::HeaderValue::from_str(header.1.as_str())?,
119 );
120 }
121 }
122 Ok(converted.into_parts().0)
123 }
124 fn url(&self) -> Result<Url> {
125 Ok(self.req.url().clone())
126 }
127 fn method(&self) -> Result<String> {
128 Ok(self.req.method().as_ref().to_string())
129 }
130 async fn remote_fetch(&mut self) -> Result<HttpResponse> {
131 let url = self.req.url().clone();
132 let mut res =
133 self.next.run(self.req.clone(), self.client.clone()).await?;
134 let mut headers = HashMap::new();
135 for header in res.iter() {
136 headers.insert(
137 header.0.as_str().to_owned(),
138 header.1.as_str().to_owned(),
139 );
140 }
141 let status = res.status().into();
142 let version = res.version().unwrap_or(Version::Http1_1);
143 let body: Vec<u8> = res.body_bytes().await?;
144 Ok(HttpResponse {
145 body,
146 headers,
147 status,
148 url,
149 version: version.try_into()?,
150 })
151 }
152}
153
154fn to_http_types_error(e: BoxError) -> http_types::Error {
155 http_types::Error::from(anyhow!(e))
156}
157
158#[surf::utils::async_trait]
159impl<T: CacheManager> surf::middleware::Middleware for Cache<T> {
160 async fn handle(
161 &self,
162 req: Request,
163 client: Client,
164 next: Next<'_>,
165 ) -> std::result::Result<surf::Response, http_types::Error> {
166 let mut middleware = SurfMiddleware { req, client, next };
167 if self
168 .0
169 .can_cache_request(&middleware)
170 .map_err(|e| http_types::Error::from(anyhow!(e)))?
171 {
172 let res =
173 self.0.run(middleware).await.map_err(to_http_types_error)?;
174 let mut converted = Response::new(StatusCode::Ok);
175 for header in &res.headers {
176 let val =
177 HeaderValue::from_bytes(header.1.as_bytes().to_vec())?;
178 converted.insert_header(header.0.as_str(), val);
179 }
180 converted.set_status(res.status.try_into()?);
181 converted.set_version(Some(res.version.into()));
182 converted.set_body(res.body);
183 Ok(surf::Response::from(converted))
184 } else {
185 self.0
186 .run_no_cache(&mut middleware)
187 .await
188 .map_err(to_http_types_error)?;
189 let mut res =
190 middleware.next.run(middleware.req, middleware.client).await?;
191 let miss = HitOrMiss::MISS.to_string();
192 res.append_header(XCACHE, miss.clone());
193 res.append_header(XCACHELOOKUP, miss);
194 Ok(res)
195 }
196 }
197}
198
199#[cfg(test)]
200mod test;