actix_web_lab/
clear_site_data.rs

1//! Cache-Control typed header.
2//!
3//! See [`CacheControl`] docs.
4
5use std::{
6    convert::Infallible,
7    fmt,
8    str::{self, FromStr},
9};
10
11use actix_http::{
12    HttpMessage,
13    error::ParseError,
14    header::{
15        CLEAR_SITE_DATA, Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue,
16    },
17};
18
19use crate::header::{fmt_comma_delimited_quoted_strings, from_comma_delimited_quoted_strings};
20
21/// The `Clear-Site-Data` header, defined in the [W3C Clear-Site-Data spec].
22///
23/// Contains a list of [directives](ClearSiteDataDirective) for clearing out various types of data
24/// from the user agent.
25///
26/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data)
27///
28/// # ABNF
29///
30/// ```text
31/// Clear-Site-Data = 1#( quoted-string )
32/// ```
33///
34/// # Sample Values
35///
36/// - `"cache"`
37/// - `"cache", "cookies"`
38/// - `"*"`
39///
40/// # Examples
41///
42/// ```
43/// use actix_web::HttpResponse;
44/// use actix_web_lab::header::{ClearSiteData, ClearSiteDataDirective};
45///
46/// let mut res = HttpResponse::Ok();
47/// res.insert_header(ClearSiteData(vec![ClearSiteDataDirective::All]));
48///
49/// // shortcut for the all ("*", wildcard) directive
50/// let mut res = HttpResponse::Ok();
51/// res.insert_header(ClearSiteData::all());
52/// ```
53///
54/// [W3C Clear-Site-Data spec]: https://www.w3.org/TR/clear-site-data
55#[derive(Debug, Clone, PartialEq)]
56pub struct ClearSiteData(pub Vec<ClearSiteDataDirective>);
57
58impl_more::forward_deref_and_mut!(ClearSiteData => [ClearSiteDataDirective]);
59
60impl ClearSiteData {
61    /// Constructs a Clear-Site-Data header containing the wildcard directive indicating that all
62    /// data types should be cleared.
63    #[doc(alias = "wildcard")]
64    pub fn all() -> Self {
65        Self(vec![ClearSiteDataDirective::All])
66    }
67}
68
69impl fmt::Display for ClearSiteData {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        fmt_comma_delimited_quoted_strings(f, self.0.iter())
72    }
73}
74
75impl TryIntoHeaderValue for ClearSiteData {
76    type Error = InvalidHeaderValue;
77
78    fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
79        HeaderValue::try_from(self.to_string())
80    }
81}
82
83impl Header for ClearSiteData {
84    fn name() -> HeaderName {
85        CLEAR_SITE_DATA
86    }
87
88    fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError> {
89        let headers = msg.headers().get_all(Self::name());
90
91        let items = from_comma_delimited_quoted_strings(headers)?;
92
93        if items.is_empty() {
94            return Err(ParseError::Header);
95        }
96
97        Ok(ClearSiteData(items))
98    }
99}
100
101/// Directives contained in a [`ClearSiteData`] header.
102///
103/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#directives)
104#[derive(Debug, Clone, PartialEq)]
105#[non_exhaustive]
106pub enum ClearSiteDataDirective {
107    /// Indicates that the server wishes to clear all types of data for the origin of the response.
108    ///
109    /// If more data types are added in future versions of this header, they will also be covered by
110    /// it.
111    #[doc(alias = "wildcard")]
112    All,
113
114    /// Indicates that the server wishes to remove locally cached data for the origin of the
115    /// response URL.
116    ///
117    /// Depending on the browser, this might also clear out things like pre-rendered pages, script
118    /// caches, WebGL shader caches, or address bar suggestions.
119    ///
120    /// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#cache)
121    Cache,
122
123    /// Indicates that the server wishes to remove all client hints (requested via Accept-CH) stored
124    /// for the origin of the response URL.
125    ///
126    /// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#clienthints)
127    ClientHints,
128
129    /// Indicates that the server wishes to remove all cookies for the origin of the response URL.
130    ///
131    /// HTTP authentication credentials are also cleared out. This affects the entire registered
132    /// domain, including subdomains.
133    ///
134    /// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#cookies)
135    Cookies,
136
137    /// Indicates that the server wishes to remove all DOM storage for the origin of the response
138    /// URL.
139    ///
140    /// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#storage)
141    Storage,
142
143    /// Indicates that the server wishes to reload all browsing contexts for the origin of the
144    /// response.
145    ///
146    /// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#executioncontexts)
147    ExecutionContexts,
148}
149
150impl ClearSiteDataDirective {
151    const fn directive(&self) -> &'static str {
152        use ClearSiteDataDirective::*;
153
154        match self {
155            All => "*",
156            Cache => "cache",
157            ClientHints => "clientHints",
158            Cookies => "cookies",
159            Storage => "storage",
160            ExecutionContexts => "executionContexts",
161        }
162    }
163}
164
165impl fmt::Display for ClearSiteDataDirective {
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        f.write_str(self.directive())
168    }
169}
170
171impl FromStr for ClearSiteDataDirective {
172    type Err = Option<Infallible>;
173
174    fn from_str(dir: &str) -> Result<Self, Self::Err> {
175        use ClearSiteDataDirective::*;
176
177        match () {
178            _ if dir == All.directive() => Ok(All),
179            _ if dir == Cache.directive() => Ok(Cache),
180            _ if dir == ClientHints.directive() => Ok(ClientHints),
181            _ if dir == Cookies.directive() => Ok(Cookies),
182            _ if dir == Storage.directive() => Ok(Storage),
183            _ if dir == ExecutionContexts.directive() => Ok(ExecutionContexts),
184
185            _ => Err(None),
186        }
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn deref() {
196        let mut cache_ctrl = ClearSiteData(vec![]);
197        let _: &[ClearSiteDataDirective] = &cache_ctrl;
198        let _: &mut [ClearSiteDataDirective] = &mut cache_ctrl;
199    }
200}
201
202#[cfg(test)]
203crate::test::header_test_module! {
204    ClearSiteData,
205    tests_parse_and_format {
206        header_round_trip_test!(no_headers, [b""; 0], None);
207        header_round_trip_test!(empty_header, [b""; 1], None);
208        header_round_trip_test!(bad_syntax, [b"foo="], None);
209        header_round_trip_test!(bad_syntax_non_quoted, [b"cache"], None);
210
211        header_round_trip_test!(
212            wildcard,
213            [b"\"*\""],
214            Some(ClearSiteData(vec![
215                ClearSiteDataDirective::All,
216            ]))
217        );
218
219        header_round_trip_test!(
220            single_header,
221            [&b"\"cache\""[..]],
222            Some(ClearSiteData(vec![
223                ClearSiteDataDirective::Cache,
224            ]))
225        );
226
227        header_round_trip_test!(
228            single_header_multiple_directives,
229            [b"\"cache\", \"storage\""],
230            Some(ClearSiteData(vec![
231                ClearSiteDataDirective::Cache,
232                ClearSiteDataDirective::Storage,
233            ]))
234        );
235
236        header_round_trip_test!(
237            multiple_headers,
238            [&b"\"cache\""[..], &b"\"cookies\""[..]],
239            Some(ClearSiteData(vec![
240                ClearSiteDataDirective::Cache,
241                ClearSiteDataDirective::Cookies,
242            ]))
243        );
244    }
245}