1
2use crate::header::Header;
3use crate::request::{METHOD, Request};
4use crate::response::Error;
5use crate::server_config::ServerConfig;
6
7#[derive(PartialEq, Eq, Clone, Debug)]
8pub struct Cors {
9 pub allow_all: bool,
10 pub allow_origins: Vec<String>,
11 pub allow_methods: Vec<String>,
12 pub allow_headers: Vec<String>,
13 pub allow_credentials: bool,
14 pub expose_headers: Vec<String>,
15 pub max_age: String,
16}
17
18impl Cors {
19 pub const MAX_AGE: &'static str = "86400";
20
21 pub fn origin_is_allowed(allowed: &str, origin: &str) -> bool {
27 let origin = origin.trim();
28 !origin.is_empty() && allowed.split(',').any(|candidate| candidate.trim().eq_ignore_ascii_case(origin))
29 }
30
31 pub fn get_vary_header_value() -> String {
32 Header::_ORIGIN.to_string()
33 }
34
35 pub fn allow_all(request: &Request) -> Result<Vec<Header>, Error> {
36 let mut headers : Vec<Header> = vec![];
37 let origin = request.header(Header::_ORIGIN);
38 if origin.is_some() {
39 let allow_origin = Header {
40 name: Header::_ACCESS_CONTROL_ALLOW_ORIGIN.into(),
41 value: origin.unwrap().value.to_string().into()
42 };
43 headers.push(allow_origin);
44
45 let allow_credentials = Header {
46 name: Header::_ACCESS_CONTROL_ALLOW_CREDENTIALS.into(),
47 value: "true".into()
48 };
49 headers.push(allow_credentials);
50
51 let is_options = request.method == METHOD.options;
52 if is_options {
53 let method = request.header(Header::_ACCESS_CONTROL_REQUEST_METHOD);
54 if method.is_some() {
55 let allow_method = Header {
56 name: Header::_ACCESS_CONTROL_ALLOW_METHODS.into(),
57 value: method.unwrap().value.to_string().into()
58 };
59 headers.push(allow_method);
60 }
61
62 let access_control_request_headers = request.header(Header::_ACCESS_CONTROL_REQUEST_HEADERS);
63 if access_control_request_headers.is_some() {
64 let request_headers = access_control_request_headers.unwrap();
65 let allow_headers = Header {
66 name: Header::_ACCESS_CONTROL_ALLOW_HEADERS.into(),
67 value: request_headers.value.to_lowercase().into(),
68 };
69 headers.push(allow_headers);
70
71 let expose_headers = Header {
72 name: Header::_ACCESS_CONTROL_EXPOSE_HEADERS.into(),
73 value: request_headers.value.to_lowercase().into(),
74 };
75 headers.push(expose_headers);
76 }
77
78 let max_age = Header {
79 name: Header::_ACCESS_CONTROL_MAX_AGE.into(),
80 value: Cors::MAX_AGE.into()
81 };
82 headers.push(max_age);
83 }
84
85 }
86
87 Ok(headers)
88 }
89
90 pub fn _process(request: &Request, cors: &Cors) -> Result<Vec<Header>, Error> {
91 let mut headers : Vec<Header> = vec![];
92
93 let allow_origins = cors.allow_origins.join(",");
94 let boxed_origin = request.header(Header::_ORIGIN);
95
96 if boxed_origin.is_none() {
97 return Ok(headers)
98 }
99
100 let origin = boxed_origin.unwrap();
101 let origin_value = format!("{}", origin.value);
102
103 let is_valid_origin = Cors::origin_is_allowed(&allow_origins, &origin_value);
104 if !is_valid_origin {
105 return Ok(headers)
106 }
107
108 let allow_origin = Header {
109 name: Header::_ACCESS_CONTROL_ALLOW_ORIGIN.into(),
110 value: origin_value.into()
111 };
112 headers.push(allow_origin);
113
114 if cors.allow_credentials {
115 let allow_credentials = Header {
116 name: Header::_ACCESS_CONTROL_ALLOW_CREDENTIALS.into(),
117 value: cors.allow_credentials.to_string().into()
118 };
119 headers.push(allow_credentials);
120 }
121
122 if !cors.expose_headers.is_empty() {
124 headers.push(Header {
125 name: Header::_ACCESS_CONTROL_EXPOSE_HEADERS.into(),
126 value: cors.expose_headers.join(",").to_lowercase().into(),
127 });
128 }
129
130 let is_options = request.method == METHOD.options;
131 if is_options {
132 let methods = cors.allow_methods.join(",");
133 let allow_methods = Header {
134 name: Header::_ACCESS_CONTROL_ALLOW_METHODS.into(),
135 value: methods.into()
136 };
137 headers.push(allow_methods);
138
139 let allow_headers_value = cors.allow_headers.join(",");
140 let allow_headers = Header {
141 name: Header::_ACCESS_CONTROL_ALLOW_HEADERS.into(),
142 value: allow_headers_value.to_lowercase().into()
143 };
144 headers.push(allow_headers);
145
146 let max_age = Header {
147 name: Header::_ACCESS_CONTROL_MAX_AGE.into(),
148 value: cors.max_age.to_string().into()
149 };
150 headers.push(max_age);
151 }
152
153 Ok(headers)
154 }
155
156 pub fn get_headers_from_config(request: &Request, config: &ServerConfig) -> Vec<Header> {
162 if config.cors_allow_all {
163 return Cors::allow_all(request).unwrap_or_default();
164 }
165 Cors::process_using_config(request, config).unwrap_or_default()
166 }
167
168 fn process_using_config(request: &Request, config: &ServerConfig) -> Result<Vec<Header>, Error> {
169 let mut headers: Vec<Header> = vec![];
170
171 let boxed_origin = request.header(Header::_ORIGIN);
172 if boxed_origin.is_none() {
173 return Ok(headers);
174 }
175 let origin_value = boxed_origin.unwrap().value.clone();
176
177 if !Cors::origin_is_allowed(&config.cors_allow_origins, &origin_value) {
178 return Ok(headers);
179 }
180
181 headers.push(Header {
182 name: Header::_ACCESS_CONTROL_ALLOW_ORIGIN.into(),
183 value: origin_value,
184 });
185
186 let credentials_str = &config.cors_allow_credentials;
187 if credentials_str.eq_ignore_ascii_case("true") {
188 headers.push(Header {
189 name: Header::_ACCESS_CONTROL_ALLOW_CREDENTIALS.into(),
190 value: "true".into(),
191 });
192 }
193
194 if !config.cors_expose_headers.is_empty() {
197 headers.push(Header {
198 name: Header::_ACCESS_CONTROL_EXPOSE_HEADERS.into(),
199 value: config.cors_expose_headers.to_lowercase().into(),
200 });
201 }
202
203 if request.method == METHOD.options {
204 if !config.cors_allow_methods.is_empty() {
205 headers.push(Header {
206 name: Header::_ACCESS_CONTROL_ALLOW_METHODS.into(),
207 value: config.cors_allow_methods.clone().into(),
208 });
209 }
210 if !config.cors_allow_headers.is_empty() {
211 headers.push(Header {
212 name: Header::_ACCESS_CONTROL_ALLOW_HEADERS.into(),
213 value: config.cors_allow_headers.to_lowercase().into(),
214 });
215 }
216 if !config.cors_max_age.is_empty() {
217 headers.push(Header {
218 name: Header::_ACCESS_CONTROL_MAX_AGE.into(),
219 value: config.cors_max_age.clone().into(),
220 });
221 }
222 }
223
224 Ok(headers)
225 }
226
227 pub fn process_using_default_config(request: &Request) -> Result<Vec<Header>, Error> {
233 let config = ServerConfig::current();
234 Self::process_using_config(request, &config)
235 }
236
237 pub fn get_headers(request: &Request) -> Vec<Header> {
241 let config = ServerConfig::current();
242 Cors::get_headers_from_config(request, &config)
243 }
244}