Skip to main content

trillium_caching_headers/
caching_conn_ext.rs

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