trillium_caching_headers/
caching_conn_ext.rs

1use crate::CacheControlHeader;
2use etag::EntityTag;
3use std::{str::FromStr, time::SystemTime};
4use trillium::{HeaderName, KnownHeaderName};
5
6/// Provides an extension trait for both [`trillium::Headers`] and
7/// also [`trillium::Conn`] for setting and getting various parsed
8/// caching headers.
9pub trait CachingHeadersExt: Sized {
10    /// returns an [`EntityTag`] if these headers contain an `Etag` header.
11    fn etag(&self) -> Option<EntityTag>;
12    /// sets an etag header from an EntityTag.
13    fn set_etag(&mut self, entity_tag: &EntityTag);
14
15    /// returns a parsed timestamp if these headers contain a `Last-Modified` header.
16    fn last_modified(&self) -> Option<SystemTime>;
17    /// sets a formatted `Last-Modified` header from a timestamp.
18    fn set_last_modified(&mut self, system_time: SystemTime);
19
20    /// returns a parsed [`CacheControlHeader`] if these headers
21    /// include a `Cache-Control` header. Note that if this is called
22    /// on a [`Conn`], it returns the request [`Cache-Control`]
23    /// header.
24    fn cache_control(&self) -> Option<CacheControlHeader>;
25    /// sets a `Cache-Control` header on these headers. Note that this
26    /// is valid in both request and response contexts, and specific
27    /// directives have different meanings.
28    fn set_cache_control(&mut self, cache_control: impl Into<CacheControlHeader>);
29
30    /// returns a parsed `If-Modified-Since` header if one exists
31    fn if_modified_since(&self) -> Option<SystemTime>;
32    /// returns a parsed [`EntityTag`] header if there is an `If-None-Match` header.
33    fn if_none_match(&self) -> Option<EntityTag>;
34
35    /// sets the Vary header to a collection of Into<HeaderName>
36    fn set_vary<I, N>(&mut self, vary: I)
37    where
38        I: IntoIterator<Item = N>,
39        N: Into<HeaderName<'static>>;
40
41    /// chainable method to set cache control and return self. primarily useful on Conn
42    fn with_cache_control(mut self, cache_control: impl Into<CacheControlHeader>) -> Self {
43        self.set_cache_control(cache_control);
44        self
45    }
46
47    /// chainable method to set last modified and return self. primarily useful on Conn
48    fn with_last_modified(mut self, system_time: SystemTime) -> Self {
49        self.set_last_modified(system_time);
50        self
51    }
52
53    /// chainable method to set etag and return self. primarily useful on Conn
54    fn with_etag(mut self, entity_tag: &EntityTag) -> Self {
55        self.set_etag(entity_tag);
56        self
57    }
58
59    /// chainable method to set vary and return self. primarily useful on Conn
60    fn with_vary<I, N>(mut self, vary: I) -> Self
61    where
62        I: IntoIterator<Item = N>,
63        N: Into<HeaderName<'static>>,
64    {
65        self.set_vary(vary);
66        self
67    }
68}
69
70impl CachingHeadersExt for trillium::Conn {
71    fn etag(&self) -> Option<EntityTag> {
72        self.inner().response_headers().etag()
73    }
74
75    fn set_etag(&mut self, entity_tag: &EntityTag) {
76        self.inner_mut().response_headers_mut().set_etag(entity_tag)
77    }
78
79    fn last_modified(&self) -> Option<SystemTime> {
80        self.inner().response_headers().last_modified()
81    }
82
83    fn set_last_modified(&mut self, system_time: SystemTime) {
84        self.inner_mut()
85            .response_headers_mut()
86            .set_last_modified(system_time)
87    }
88
89    fn cache_control(&self) -> Option<CacheControlHeader> {
90        self.inner().request_headers().cache_control()
91    }
92
93    fn set_cache_control(&mut self, cache_control: impl Into<CacheControlHeader>) {
94        self.inner_mut()
95            .response_headers_mut()
96            .set_cache_control(cache_control)
97    }
98
99    fn if_modified_since(&self) -> Option<SystemTime> {
100        self.inner().request_headers().if_modified_since()
101    }
102
103    fn if_none_match(&self) -> Option<EntityTag> {
104        self.inner().request_headers().if_none_match()
105    }
106
107    fn set_vary<I, N>(&mut self, vary: I)
108    where
109        I: IntoIterator<Item = N>,
110        N: Into<HeaderName<'static>>,
111    {
112        self.inner_mut().response_headers_mut().set_vary(vary)
113    }
114}
115
116impl CachingHeadersExt for trillium::Headers {
117    fn etag(&self) -> Option<EntityTag> {
118        self.get_str(KnownHeaderName::Etag)
119            .and_then(|etag| etag.parse().ok())
120    }
121
122    fn set_etag(&mut self, entity_tag: &EntityTag) {
123        let string = entity_tag.to_string();
124        self.insert(KnownHeaderName::Etag, string);
125    }
126
127    fn last_modified(&self) -> Option<SystemTime> {
128        self.get_str(KnownHeaderName::LastModified)
129            .and_then(|x| httpdate::parse_http_date(x).ok())
130    }
131
132    fn set_last_modified(&mut self, system_time: SystemTime) {
133        self.insert(
134            KnownHeaderName::LastModified,
135            httpdate::fmt_http_date(system_time),
136        );
137    }
138
139    fn cache_control(&self) -> Option<CacheControlHeader> {
140        self.get_str(KnownHeaderName::CacheControl)
141            .and_then(|cc| cc.parse().ok())
142    }
143
144    fn set_cache_control(&mut self, cache_control: impl Into<CacheControlHeader>) {
145        self.insert(
146            KnownHeaderName::CacheControl,
147            cache_control.into().to_string(),
148        );
149    }
150
151    fn if_modified_since(&self) -> Option<SystemTime> {
152        self.get_str(KnownHeaderName::IfModifiedSince)
153            .and_then(|h| httpdate::parse_http_date(h).ok())
154    }
155
156    fn if_none_match(&self) -> Option<EntityTag> {
157        self.get_str(KnownHeaderName::IfNoneMatch)
158            .and_then(|etag| EntityTag::from_str(etag).ok())
159    }
160
161    fn set_vary<I, N>(&mut self, vary: I)
162    where
163        I: IntoIterator<Item = N>,
164        N: Into<HeaderName<'static>>,
165    {
166        self.insert(
167            KnownHeaderName::Vary,
168            vary.into_iter()
169                .map(|n| n.into().to_string())
170                .collect::<Vec<_>>()
171                .join(","),
172        );
173    }
174}