1use crate::headers::Headers;
4
5pub const DEFAULT_ALLOWED_HEADERS: [&str; 5] = [
7 "Content-Type",
8 "Accept",
9 "Authorization",
10 "If-Match",
11 "If-None-Match",
12];
13
14pub const DEFAULT_EXPOSED_HEADERS: [&str; 4] = ["ETag", "Link", "Location", "Allow"];
16
17pub const DEFAULT_METHODS: [&str; 7] = ["GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"];
19
20#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct CorsPolicy {
26 pub origins: Vec<String>,
28 pub methods: Vec<String>,
30 pub allowed_headers: Vec<String>,
32 pub exposed_headers: Vec<String>,
34 pub credentials: bool,
36 pub max_age: u32,
38}
39
40impl Default for CorsPolicy {
41 fn default() -> Self {
42 Self::permissive()
43 }
44}
45
46impl CorsPolicy {
47 pub fn permissive() -> Self {
49 Self {
50 origins: vec!["*".to_string()],
51 methods: DEFAULT_METHODS.iter().map(|m| m.to_string()).collect(),
52 allowed_headers: DEFAULT_ALLOWED_HEADERS
53 .iter()
54 .map(|h| h.to_string())
55 .collect(),
56 exposed_headers: DEFAULT_EXPOSED_HEADERS
57 .iter()
58 .map(|h| h.to_string())
59 .collect(),
60 credentials: false,
61 max_age: 600,
62 }
63 }
64
65 pub fn with_origins<I, S>(mut self, origins: I) -> Self
76 where
77 I: IntoIterator<Item = S>,
78 S: Into<String>,
79 {
80 self.origins = origins.into_iter().map(Into::into).collect();
81 self
82 }
83
84 pub fn with_credentials(mut self, credentials: bool) -> Self {
86 self.credentials = credentials;
87 self
88 }
89
90 pub fn with_max_age(mut self, max_age: u32) -> Self {
92 self.max_age = max_age;
93 self
94 }
95}
96
97pub fn cors_headers(policy: &CorsPolicy, request_origin: Option<&str>) -> Headers {
103 let wildcard = policy.origins.iter().any(|origin| origin == "*");
104 let allow_origin = if wildcard {
105 match (policy.credentials, request_origin) {
106 (true, Some(origin)) => Some(origin.to_string()),
107 _ => Some("*".to_string()),
108 }
109 } else {
110 request_origin
111 .filter(|origin| policy.origins.iter().any(|allowed| allowed == origin))
112 .map(str::to_string)
113 };
114
115 let Some(allow_origin) = allow_origin else {
116 return Headers::new();
117 };
118
119 let mut headers = Headers::from_pairs([
120 ("Access-Control-Allow-Origin", allow_origin),
121 ("Access-Control-Allow-Methods", policy.methods.join(", ")),
122 (
123 "Access-Control-Allow-Headers",
124 policy.allowed_headers.join(", "),
125 ),
126 (
127 "Access-Control-Expose-Headers",
128 policy.exposed_headers.join(", "),
129 ),
130 ("Access-Control-Max-Age", policy.max_age.to_string()),
131 ]);
132 if policy.credentials {
133 headers.insert("Access-Control-Allow-Credentials", "true");
134 }
135 headers
136}