[go: up one dir, main page]

worker/
cors.rs

1use crate::{Error, Headers, Method, Result};
2
3/// Cors struct, holding cors configuration
4#[derive(Debug, Clone)]
5pub struct Cors {
6    credentials: bool,
7    max_age: Option<u32>,
8    origins: Vec<String>,
9    methods: Vec<Method>,
10    allowed_headers: Vec<String>,
11    exposed_headers: Vec<String>,
12}
13
14/// Creates a default cors configuration, which will do nothing.
15impl Default for Cors {
16    fn default() -> Self {
17        Self {
18            credentials: false,
19            max_age: None,
20            origins: vec![],
21            methods: vec![],
22            allowed_headers: vec![],
23            exposed_headers: vec![],
24        }
25    }
26}
27
28impl Cors {
29    /// `new` constructor for convenience; does the same as `Self::default()`.
30    pub fn new() -> Self {
31        Self::default()
32    }
33
34    /// Configures whether cors is allowed to share credentials or not.
35    pub fn with_credentials(mut self, credentials: bool) -> Self {
36        self.credentials = credentials;
37        self
38    }
39
40    /// Configures how long cors is allowed to cache a preflight-response.
41    pub fn with_max_age(mut self, max_age: u32) -> Self {
42        self.max_age = Some(max_age);
43        self
44    }
45
46    /// Configures which origins are allowed for cors.
47    pub fn with_origins<S: Into<String>, V: IntoIterator<Item = S>>(mut self, origins: V) -> Self {
48        self.origins = origins
49            .into_iter()
50            .map(|item| item.into())
51            .collect::<Vec<String>>();
52        self
53    }
54
55    /// Configures which methods are allowed for cors.
56    pub fn with_methods<V: IntoIterator<Item = Method>>(mut self, methods: V) -> Self {
57        self.methods = methods.into_iter().collect();
58        self
59    }
60
61    /// Configures which headers are allowed for cors.
62    pub fn with_allowed_headers<S: Into<String>, V: IntoIterator<Item = S>>(
63        mut self,
64        headers: V,
65    ) -> Self {
66        self.allowed_headers = headers
67            .into_iter()
68            .map(|item| item.into())
69            .collect::<Vec<String>>();
70        self
71    }
72
73    /// Configures which headers the client is allowed to access.
74    pub fn with_exposed_headers<S: Into<String>, V: IntoIterator<Item = S>>(
75        mut self,
76        headers: V,
77    ) -> Self {
78        self.exposed_headers = headers
79            .into_iter()
80            .map(|item| item.into())
81            .collect::<Vec<String>>();
82        self
83    }
84
85    /// Applies the cors configuration to response headers.
86    pub fn apply_headers(&self, headers: &mut Headers) -> Result<()> {
87        if self.credentials {
88            headers.set("Access-Control-Allow-Credentials", "true")?;
89        }
90        if let Some(ref max_age) = self.max_age {
91            headers.set("Access-Control-Max-Age", format!("{max_age}").as_str())?;
92        }
93        if !self.origins.is_empty() {
94            headers.set(
95                "Access-Control-Allow-Origin",
96                concat_vec_to_string(self.origins.as_slice())?.as_str(),
97            )?;
98        }
99        if !self.methods.is_empty() {
100            headers.set(
101                "Access-Control-Allow-Methods",
102                concat_vec_to_string(self.methods.as_slice())?.as_str(),
103            )?;
104        }
105        if !self.allowed_headers.is_empty() {
106            headers.set(
107                "Access-Control-Allow-Headers",
108                concat_vec_to_string(self.allowed_headers.as_slice())?.as_str(),
109            )?;
110        }
111        if !self.exposed_headers.is_empty() {
112            headers.set(
113                "Access-Control-Expose-headers",
114                concat_vec_to_string(self.exposed_headers.as_slice())?.as_str(),
115            )?;
116        }
117        Ok(())
118    }
119}
120
121fn concat_vec_to_string<S: AsRef<str>>(vec: &[S]) -> Result<String> {
122    let str = vec.iter().fold("".to_owned(), |mut init, item| {
123        init.push(',');
124        init.push_str(item.as_ref());
125        init
126    });
127    if !str.is_empty() {
128        Ok(str[1..].to_string())
129    } else {
130        Err(Error::RustError(
131            "Tried to concat header values without values.".to_string(),
132        ))
133    }
134}