http_types/cache/clear_site_data/
mod.rs

1//! Clear browsing data (cookies, storage, cache) associated with the
2//! requesting website
3
4use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, CLEAR_SITE_DATA};
5
6use std::fmt::{self, Debug, Write};
7use std::iter::Iterator;
8use std::option;
9use std::slice;
10use std::str::FromStr;
11
12mod directive;
13
14pub use directive::ClearDirective;
15
16/// Clear browsing data (cookies, storage, cache) associated with the
17/// requesting website.
18///
19/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data)
20///
21/// # Specifications
22///
23/// - [Clear Site Data](https://w3c.github.io/webappsec-clear-site-data/)
24///
25/// # Examples
26///
27/// ```
28/// # fn main() -> http_types::Result<()> {
29/// #
30/// use http_types::Response;
31/// use http_types::cache::{ClearSiteData, ClearDirective};
32///
33/// let mut entries = ClearSiteData::new();
34/// entries.push(ClearDirective::Cache);
35/// entries.push(ClearDirective::Cookies);
36///
37/// let mut res = Response::new(200);
38/// entries.apply(&mut res);
39///
40/// let entries = ClearSiteData::from_headers(res)?.unwrap();
41/// let mut entries = entries.iter();
42/// assert_eq!(entries.next().unwrap(), &ClearDirective::Cache);
43/// assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies);
44/// #
45/// # Ok(()) }
46/// ```
47pub struct ClearSiteData {
48    entries: Vec<ClearDirective>,
49    wildcard: bool,
50}
51
52impl ClearSiteData {
53    /// Create a new instance of `ClearSiteData`.
54    pub fn new() -> Self {
55        Self {
56            entries: vec![],
57            wildcard: false,
58        }
59    }
60
61    /// Create a new instance from headers.
62    pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
63        let mut entries = vec![];
64        let header_values = match headers.as_ref().get(CLEAR_SITE_DATA) {
65            Some(headers) => headers,
66            None => return Ok(None),
67        };
68
69        let mut wildcard = false;
70        for value in header_values {
71            for part in value.as_str().trim().split(',') {
72                let part = part.trim();
73                if part == r#""*""# {
74                    wildcard = true;
75                    continue;
76                }
77                entries.push(ClearDirective::from_str(part)?);
78            }
79        }
80
81        Ok(Some(Self { entries, wildcard }))
82    }
83
84    /// Sets the `If-Match` header.
85    pub fn apply(&self, mut headers: impl AsMut<Headers>) {
86        headers.as_mut().insert(CLEAR_SITE_DATA, self.value());
87    }
88
89    /// Get the `HeaderName`.
90    pub fn name(&self) -> HeaderName {
91        CLEAR_SITE_DATA
92    }
93
94    /// Get the `HeaderValue`.
95    pub fn value(&self) -> HeaderValue {
96        let mut output = String::new();
97        for (n, etag) in self.entries.iter().enumerate() {
98            match n {
99                0 => write!(output, "{}", etag.to_string()).unwrap(),
100                _ => write!(output, ", {}", etag.to_string()).unwrap(),
101            };
102        }
103
104        if self.wildcard {
105            match output.len() {
106                0 => write!(output, r#""*""#).unwrap(),
107                _ => write!(output, r#", "*""#).unwrap(),
108            };
109        }
110
111        // SAFETY: the internal string is validated to be ASCII.
112        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
113    }
114
115    /// Push a directive into the list of entries.
116    pub fn push(&mut self, directive: impl Into<ClearDirective>) {
117        self.entries.push(directive.into());
118    }
119
120    /// Returns `true` if a wildcard directive was set.
121    pub fn wildcard(&self) -> bool {
122        self.wildcard
123    }
124
125    /// Set the wildcard directive.
126    pub fn set_wildcard(&mut self, wildcard: bool) {
127        self.wildcard = wildcard
128    }
129
130    /// An iterator visiting all server entries.
131    pub fn iter(&self) -> Iter<'_> {
132        Iter {
133            inner: self.entries.iter(),
134        }
135    }
136
137    /// An iterator visiting all server entries.
138    pub fn iter_mut(&mut self) -> IterMut<'_> {
139        IterMut {
140            inner: self.entries.iter_mut(),
141        }
142    }
143}
144
145impl IntoIterator for ClearSiteData {
146    type Item = ClearDirective;
147    type IntoIter = IntoIter;
148
149    #[inline]
150    fn into_iter(self) -> Self::IntoIter {
151        IntoIter {
152            inner: self.entries.into_iter(),
153        }
154    }
155}
156
157impl<'a> IntoIterator for &'a ClearSiteData {
158    type Item = &'a ClearDirective;
159    type IntoIter = Iter<'a>;
160
161    #[inline]
162    fn into_iter(self) -> Self::IntoIter {
163        self.iter()
164    }
165}
166
167impl<'a> IntoIterator for &'a mut ClearSiteData {
168    type Item = &'a mut ClearDirective;
169    type IntoIter = IterMut<'a>;
170
171    #[inline]
172    fn into_iter(self) -> Self::IntoIter {
173        self.iter_mut()
174    }
175}
176
177/// A borrowing iterator over entries in `ClearSiteData`.
178#[derive(Debug)]
179pub struct IntoIter {
180    inner: std::vec::IntoIter<ClearDirective>,
181}
182
183impl Iterator for IntoIter {
184    type Item = ClearDirective;
185
186    fn next(&mut self) -> Option<Self::Item> {
187        self.inner.next()
188    }
189
190    #[inline]
191    fn size_hint(&self) -> (usize, Option<usize>) {
192        self.inner.size_hint()
193    }
194}
195
196/// A lending iterator over entries in `ClearSiteData`.
197#[derive(Debug)]
198pub struct Iter<'a> {
199    inner: slice::Iter<'a, ClearDirective>,
200}
201
202impl<'a> Iterator for Iter<'a> {
203    type Item = &'a ClearDirective;
204
205    fn next(&mut self) -> Option<Self::Item> {
206        self.inner.next()
207    }
208
209    #[inline]
210    fn size_hint(&self) -> (usize, Option<usize>) {
211        self.inner.size_hint()
212    }
213}
214
215/// A mutable iterator over entries in `ClearSiteData`.
216#[derive(Debug)]
217pub struct IterMut<'a> {
218    inner: slice::IterMut<'a, ClearDirective>,
219}
220
221impl<'a> Iterator for IterMut<'a> {
222    type Item = &'a mut ClearDirective;
223
224    fn next(&mut self) -> Option<Self::Item> {
225        self.inner.next()
226    }
227
228    #[inline]
229    fn size_hint(&self) -> (usize, Option<usize>) {
230        self.inner.size_hint()
231    }
232}
233
234impl ToHeaderValues for ClearSiteData {
235    type Iter = option::IntoIter<HeaderValue>;
236    fn to_header_values(&self) -> crate::Result<Self::Iter> {
237        // A HeaderValue will always convert into itself.
238        Ok(self.value().to_header_values().unwrap())
239    }
240}
241
242impl Debug for ClearSiteData {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        let mut list = f.debug_list();
245        for directive in &self.entries {
246            list.entry(directive);
247        }
248        list.finish()
249    }
250}
251
252#[cfg(test)]
253mod test {
254    use crate::cache::{ClearDirective, ClearSiteData};
255    use crate::Response;
256
257    #[test]
258    fn smoke() -> crate::Result<()> {
259        let mut entries = ClearSiteData::new();
260        entries.push(ClearDirective::Cache);
261        entries.push(ClearDirective::Cookies);
262
263        let mut res = Response::new(200);
264        entries.apply(&mut res);
265
266        let entries = ClearSiteData::from_headers(res)?.unwrap();
267        let mut entries = entries.iter();
268        assert_eq!(entries.next().unwrap(), &ClearDirective::Cache);
269        assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies);
270        Ok(())
271    }
272
273    #[test]
274    fn wildcard() -> crate::Result<()> {
275        let mut entries = ClearSiteData::new();
276        entries.push(ClearDirective::Cache);
277        entries.set_wildcard(true);
278
279        let mut res = Response::new(200);
280        entries.apply(&mut res);
281
282        let entries = ClearSiteData::from_headers(res)?.unwrap();
283        assert!(entries.wildcard());
284        let mut entries = entries.iter();
285        assert_eq!(entries.next().unwrap(), &ClearDirective::Cache);
286        Ok(())
287    }
288
289    #[test]
290    fn parse_quotes_correctly() -> crate::Result<()> {
291        let mut res = Response::new(200);
292        res.insert_header("clear-site-data", r#""cookies""#);
293
294        let entries = ClearSiteData::from_headers(res)?.unwrap();
295        assert!(!entries.wildcard());
296        let mut entries = entries.iter();
297        assert_eq!(entries.next().unwrap(), &ClearDirective::Cookies);
298
299        let mut res = Response::new(200);
300        res.insert_header("clear-site-data", r#""*""#);
301
302        let entries = ClearSiteData::from_headers(res)?.unwrap();
303        assert!(entries.wildcard());
304        let mut entries = entries.iter();
305        assert_eq!(entries.next(), None);
306        Ok(())
307    }
308}