tower_http/cors/
max_age.rs

1use std::{fmt, sync::Arc, time::Duration};
2
3use http::{
4    header::{self, HeaderName, HeaderValue},
5    request::Parts as RequestParts,
6};
7
8/// Holds configuration for how to set the [`Access-Control-Max-Age`][mdn] header.
9///
10/// See [`CorsLayer::max_age`][super::CorsLayer::max_age] for more details.
11///
12/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age
13#[derive(Clone, Default)]
14#[must_use]
15pub struct MaxAge(MaxAgeInner);
16
17impl MaxAge {
18    /// Set a static max-age value
19    ///
20    /// See [`CorsLayer::max_age`][super::CorsLayer::max_age] for more details.
21    pub fn exact(max_age: Duration) -> Self {
22        Self(MaxAgeInner::Exact(Some(max_age.as_secs().into())))
23    }
24
25    /// Set the max-age based on the preflight request parts
26    ///
27    /// See [`CorsLayer::max_age`][super::CorsLayer::max_age] for more details.
28    pub fn dynamic<F>(f: F) -> Self
29    where
30        F: Fn(&HeaderValue, &RequestParts) -> Duration + Send + Sync + 'static,
31    {
32        Self(MaxAgeInner::Fn(Arc::new(f)))
33    }
34
35    pub(super) fn to_header(
36        &self,
37        origin: Option<&HeaderValue>,
38        parts: &RequestParts,
39    ) -> Option<(HeaderName, HeaderValue)> {
40        let max_age = match &self.0 {
41            MaxAgeInner::Exact(v) => v.clone()?,
42            MaxAgeInner::Fn(c) => c(origin?, parts).as_secs().into(),
43        };
44
45        Some((header::ACCESS_CONTROL_MAX_AGE, max_age))
46    }
47}
48
49impl fmt::Debug for MaxAge {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match &self.0 {
52            MaxAgeInner::Exact(inner) => f.debug_tuple("Exact").field(inner).finish(),
53            MaxAgeInner::Fn(_) => f.debug_tuple("Fn").finish(),
54        }
55    }
56}
57
58impl From<Duration> for MaxAge {
59    fn from(max_age: Duration) -> Self {
60        Self::exact(max_age)
61    }
62}
63
64#[derive(Clone)]
65enum MaxAgeInner {
66    Exact(Option<HeaderValue>),
67    Fn(Arc<dyn for<'a> Fn(&'a HeaderValue, &'a RequestParts) -> Duration + Send + Sync + 'static>),
68}
69
70impl Default for MaxAgeInner {
71    fn default() -> Self {
72        Self::Exact(None)
73    }
74}