Skip to content

Commit

Permalink
feat: ClearSiteData header
Browse files Browse the repository at this point in the history
  • Loading branch information
robjtede committed Oct 15, 2024
1 parent b58706c commit dfebb5a
Show file tree
Hide file tree
Showing 16 changed files with 329 additions and 535 deletions.
4 changes: 2 additions & 2 deletions actix-hash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ macro_rules! body_hash_alias {
/// #
/// # // test that the documented hash size is correct
#[doc = concat!("# type Hasher = ", stringify!($digest), ";")]
#[doc = concat!("# const OutSize: usize = ", $out_size, ";")]
#[doc = concat!("# const OUT_SIZE: usize = ", $out_size, ";")]
/// # assert_eq!(
/// # digest::generic_array::GenericArray::<u8,
/// # <Hasher as digest::OutputSizeUser>::OutputSize
/// # >::default().len(),
/// # OutSize
/// # OUT_SIZE,
/// # );
/// ```
#[cfg(feature = $feature)]
Expand Down
6 changes: 6 additions & 0 deletions actix-web-lab/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

- Add `header::ClearSiteData` header.
- Add `header::ClearSiteDataDirective` type
- Remove `response::Html` responder.
- Remove `middleware::from_fn()` middleware.
- Remove `extract::ThinData` extractor.

## 0.22.0

- Add `extract::QueryDeserializeError` type.
Expand Down
2 changes: 1 addition & 1 deletion actix-web-lab/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ actix-http = "3"
actix-router = "0.5"
actix-service = "2"
actix-utils = "3"
actix-web = { version = "4.3", default-features = false }
actix-web = { version = "4.9", default-features = false }
ahash = "0.8"
arc-swap = "1.1"
bytes = "1"
Expand Down
3 changes: 2 additions & 1 deletion actix-web-lab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,11 @@

### Headers

- `StrictTransportSecurity`: Strict-Transport-Security (HSTS) configuration [(docs)](https://docs.rs/actix-web-lab/0.22.0/actix_web_lab/header/struct.StrictTransportSecurity.html)
- `CacheControl`: Cache-Control typed header with support for modern directives [(docs)](https://docs.rs/actix-web-lab/0.22.0/actix_web_lab/header/struct.CacheControl.html)
- `ClearSiteData`: Clear-Site-Data typed header [(docs)](https://docs.rs/actix-web-lab/0.22.0/actix_web_lab/header/struct.ClearSiteData.html)
- `ContentLength`: Content-Length typed header [(docs)](https://docs.rs/actix-web-lab/0.22.0/actix_web_lab/header/struct.ContentLength.html)
- `Forwarded`: Proxy and original client info [(docs)](https://docs.rs/actix-web-lab/0.22.0/actix_web_lab/header/struct.Forwarded.html)
- `StrictTransportSecurity`: Strict-Transport-Security (HSTS) configuration [(docs)](https://docs.rs/actix-web-lab/0.22.0/actix_web_lab/header/struct.StrictTransportSecurity.html)

### Body Types

Expand Down
5 changes: 1 addition & 4 deletions actix-web-lab/examples/from_fn.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
//! Shows a couple of ways to use the `from_fn` middleware.
#![allow(deprecated)]

use std::{collections::HashMap, io, rc::Rc, time::Duration};

use actix_web::{
body::MessageBody,
dev::{Service, ServiceRequest, ServiceResponse, Transform},
http::header::{self, HeaderValue, Range},
middleware::Logger,
middleware::{from_fn, Logger, Next},
web::{self, Header, Query},
App, Error, HttpResponse, HttpServer,
};
use actix_web_lab::middleware::{from_fn, Next};
use tracing::info;

async fn noop<B>(req: ServiceRequest, next: Next<B>) -> Result<ServiceResponse<B>, Error> {
Expand Down
244 changes: 244 additions & 0 deletions actix-web-lab/src/clear_site_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
//! Cache-Control typed header.
//!
//! See [`CacheControl`] docs.
use std::{
convert::Infallible,
fmt,
str::{self, FromStr},
};

use actix_http::{
error::ParseError,
header::{Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderValue},
HttpMessage,
};

use crate::header::{fmt_comma_delimited_quoted_strings, from_comma_delimited_quoted_strings};

/// The `Clear-Site-Data` header, defined in the [W3C Clear-Site-Data spec].
///
/// Contains a list of [directives](ClearSiteDataDirective) for clearing out various types of data
/// from the user agent.
///
/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data)
///
/// # ABNF
///
/// ```text
/// Clear-Site-Data = 1#( quoted-string )
/// ```
///
/// # Sample Values
///
/// - `"cache"`
/// - `"cache", "cookies"`
/// - `"*"`
///
/// # Examples
///
/// ```
/// use actix_web::HttpResponse;
/// use actix_web_lab::header::{ClearSiteData, ClearSiteDataDirective};
///
/// let mut res = HttpResponse::Ok();
/// res.insert_header(ClearSiteData(vec![ClearSiteDataDirective::All]));
///
/// // shortcut for the all ("*", wildcard) directive
/// let mut res = HttpResponse::Ok();
/// res.insert_header(ClearSiteData::all());
/// ```
///
/// [W3C Clear-Site-Data spec]: https://www.w3.org/TR/clear-site-data
#[derive(Debug, Clone, PartialEq)]
pub struct ClearSiteData(pub Vec<ClearSiteDataDirective>);

impl_more::forward_deref_and_mut!(ClearSiteData => [ClearSiteDataDirective]);

impl ClearSiteData {
/// Constructs a Clear-Site-Data header containing the wildcard directive indicating that all
/// data types should be cleared.
#[doc(alias = "wildcard")]
pub fn all() -> Self {
Self(vec![ClearSiteDataDirective::All])
}
}

impl fmt::Display for ClearSiteData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_comma_delimited_quoted_strings(f, self.0.iter())
}
}

impl TryIntoHeaderValue for ClearSiteData {
type Error = InvalidHeaderValue;

fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
HeaderValue::try_from(self.to_string())
}
}

impl Header for ClearSiteData {
fn name() -> HeaderName {
// TODO: use actix-http's constant after its next release
HeaderName::from_static("clear-site-data")
}

fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError> {
let headers = msg.headers().get_all(Self::name());

let items = from_comma_delimited_quoted_strings(headers)?;

if items.is_empty() {
return Err(ParseError::Header);
}

Ok(ClearSiteData(items))
}
}

/// Directives contained in a [`ClearSiteData`] header.
///
/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#directives)
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum ClearSiteDataDirective {
/// Indicates that the server wishes to clear all types of data for the origin of the response.
///
/// If more data types are added in future versions of this header, they will also be covered by
/// it.
#[doc(alias = "wildcard")]
All,

/// Indicates that the server wishes to remove locally cached data for the origin of the
/// response URL.
///
/// Depending on the browser, this might also clear out things like pre-rendered pages, script
/// caches, WebGL shader caches, or address bar suggestions.
///
/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#cache)
Cache,

/// Indicates that the server wishes to remove all client hints (requested via Accept-CH) stored
/// for the origin of the response URL.
///
/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#clienthints)
ClientHints,

/// Indicates that the server wishes to remove all cookies for the origin of the response URL.
///
/// HTTP authentication credentials are also cleared out. This affects the entire registered
/// domain, including subdomains.
///
/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#cookies)
Cookies,

/// Indicates that the server wishes to remove all DOM storage for the origin of the response
/// URL.
///
/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#storage)
Storage,

/// Indicates that the server wishes to reload all browsing contexts for the origin of the
/// response.
///
/// [Read more on MDN.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data#executioncontexts)
ExecutionContexts,
}

impl ClearSiteDataDirective {
const fn directive(&self) -> &'static str {
use ClearSiteDataDirective::*;

match self {
All => "*",
Cache => "cache",
ClientHints => "clientHints",
Cookies => "cookies",
Storage => "storage",
ExecutionContexts => "executionContexts",
}
}
}

impl fmt::Display for ClearSiteDataDirective {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.directive())
}
}

impl FromStr for ClearSiteDataDirective {
type Err = Option<Infallible>;

fn from_str(dir: &str) -> Result<Self, Self::Err> {
use ClearSiteDataDirective::*;

match () {
_ if dir == All.directive() => Ok(All),
_ if dir == Cache.directive() => Ok(Cache),
_ if dir == ClientHints.directive() => Ok(ClientHints),
_ if dir == Cookies.directive() => Ok(Cookies),
_ if dir == Storage.directive() => Ok(Storage),
_ if dir == ExecutionContexts.directive() => Ok(ExecutionContexts),

_ => Err(None),
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn deref() {
let mut cache_ctrl = ClearSiteData(vec![]);
let _: &[ClearSiteDataDirective] = &cache_ctrl;
let _: &mut [ClearSiteDataDirective] = &mut cache_ctrl;
}
}

#[cfg(test)]
crate::test::header_test_module! {
ClearSiteData,
tests_parse_and_format {
header_round_trip_test!(no_headers, [b""; 0], None);
header_round_trip_test!(empty_header, [b""; 1], None);
header_round_trip_test!(bad_syntax, [b"foo="], None);
header_round_trip_test!(bad_syntax_non_quoted, [b"cache"], None);

header_round_trip_test!(
wildcard,
[b"\"*\""],
Some(ClearSiteData(vec![
ClearSiteDataDirective::All,
]))
);

header_round_trip_test!(
single_header,
[&b"\"cache\""[..]],
Some(ClearSiteData(vec![
ClearSiteDataDirective::Cache,
]))
);

header_round_trip_test!(
single_header_multiple_directives,
[b"\"cache\", \"storage\""],
Some(ClearSiteData(vec![
ClearSiteDataDirective::Cache,
ClearSiteDataDirective::Storage,
]))
);

header_round_trip_test!(
multiple_headers,
[&b"\"cache\""[..], &b"\"cookies\""[..]],
Some(ClearSiteData(vec![
ClearSiteDataDirective::Cache,
ClearSiteDataDirective::Cookies,
]))
);
}
}
2 changes: 0 additions & 2 deletions actix-web-lab/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
/// An alias for [`actix_web::web::Data<T>`] with a more descriptive name.
pub type SharedData<T> = actix_web::web::Data<T>;

#[allow(deprecated)]
pub use crate::thin_data::ThinData;
pub use crate::{
body_limit::{BodyLimit, DEFAULT_BODY_LIMIT},
bytes::{Bytes, DEFAULT_BYTES_LIMIT},
Expand Down
Loading

0 comments on commit dfebb5a

Please sign in to comment.