1use lz_fnv::{Fnv1a, FnvHasher};
2use tide::{Body, Request};
3
4pub struct EtagMiddleware {}
5
6impl Default for EtagMiddleware {
7 fn default() -> Self {
24 Self {}
25 }
26}
27
28#[tide::utils::async_trait]
29impl<State: Clone + Send + Sync + 'static> tide::Middleware<State> for EtagMiddleware {
30 async fn handle(&self, req: Request<State>, next: tide::Next<'_, State>) -> tide::Result {
31 let if_none_match = req.header(tide::http::headers::IF_NONE_MATCH).cloned();
32 let mut response = next.run(req).await;
33 if let Some(existing_etag) = response.header(tide::http::headers::ETAG) {
34 if let Some(req_hash) = if_none_match {
35 if req_hash.last().as_str() == existing_etag.last().as_str() {
36 response.set_status(304);
37 response.take_body();
38 return Ok(response);
39 }
40 }
41 return Ok(response);
42 }
43 let body = response.take_body();
44 let mut hasher = Fnv1a::<u32>::new();
45 let bytes = &body.into_bytes().await?;
46 hasher.write(&bytes);
47 let hash = hasher.finish();
48 let generated_etag = base64::encode(hash.to_be_bytes());
49 response.append_header(tide::http::headers::ETAG, &generated_etag);
50 if let Some(req_hash) = if_none_match {
51 if req_hash.last().as_str() == generated_etag {
52 response.set_status(304);
53 return Ok(response);
54 }
55 }
56 response.set_body(Body::from_bytes(bytes.to_vec()));
57 Ok(response)
58 }
59}