1use std::fmt::{Debug, Formatter};
2use std::hash::Hasher;
3use std::net::IpAddr;
4use std::sync::Arc;
5use std::{cmp::Ordering, collections::HashMap};
6
7use fancy_regex::Regex;
8
9use crate::modules::Module;
10use crate::util::IpBlockList;
11
12#[non_exhaustive]
14#[derive(Clone, Debug)]
15pub enum ConditionalData {
16 IsRemoteIp(IpBlockList),
17 IsForwardedFor(IpBlockList),
18 IsNotRemoteIp(IpBlockList),
19 IsNotForwardedFor(IpBlockList),
20 IsEqual(String, String),
21 IsNotEqual(String, String),
22 IsRegex(String, Regex),
23 IsNotRegex(String, Regex),
24 IsRego(Arc<regorus::Engine>),
25}
26
27impl PartialEq for ConditionalData {
28 fn eq(&self, other: &Self) -> bool {
29 match (self, other) {
30 (Self::IsRemoteIp(v1), Self::IsRemoteIp(v2)) => v1 == v2,
31 (Self::IsForwardedFor(v1), Self::IsForwardedFor(v2)) => v1 == v2,
32 (Self::IsNotRemoteIp(v1), Self::IsNotRemoteIp(v2)) => v1 == v2,
33 (Self::IsNotForwardedFor(v1), Self::IsNotForwardedFor(v2)) => v1 == v2,
34 (Self::IsEqual(v1, v2), Self::IsEqual(v3, v4)) => v1 == v3 && v2 == v4,
35 (Self::IsNotEqual(v1, v2), Self::IsNotEqual(v3, v4)) => v1 == v3 && v2 == v4,
36 (Self::IsRegex(v1, v2), Self::IsRegex(v3, v4)) => v1 == v3 && v2.as_str() == v4.as_str(),
37 (Self::IsNotRegex(v1, v2), Self::IsNotRegex(v3, v4)) => v1 == v3 && v2.as_str() == v4.as_str(),
38 (Self::IsRego(v1), Self::IsRego(v2)) => v1.get_policies().ok() == v2.get_policies().ok(),
39 _ => false,
40 }
41 }
42}
43
44impl Eq for ConditionalData {}
45
46fn count_logical_slashes(s: &str) -> usize {
47 if s.is_empty() {
48 return 0;
50 }
51 let trimmed = s.trim_end_matches('/');
52 if trimmed.is_empty() {
53 return 1;
55 }
56
57 let mut count = 0;
58 let mut prev_was_slash = false;
59
60 for ch in trimmed.chars() {
61 if ch == '/' {
62 if !prev_was_slash {
63 count += 1;
64 prev_was_slash = true;
65 }
66 } else {
67 prev_was_slash = false;
68 }
69 }
70
71 count
72}
73
74#[derive(Clone, Debug, PartialEq, Eq)]
76pub struct Conditions {
77 pub location_prefix: String,
79
80 pub conditionals: Vec<Conditional>,
82}
83
84impl Ord for Conditions {
85 fn cmp(&self, other: &Self) -> Ordering {
86 count_logical_slashes(&self.location_prefix)
87 .cmp(&count_logical_slashes(&other.location_prefix))
88 .then_with(|| self.conditionals.len().cmp(&other.conditionals.len()))
89 }
90}
91
92impl PartialOrd for Conditions {
93 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
94 Some(self.cmp(other))
95 }
96}
97
98#[derive(Clone, Debug, PartialEq, Eq)]
100pub enum Conditional {
101 If(Vec<ConditionalData>),
103
104 IfNot(Vec<ConditionalData>),
106}
107
108#[derive(Clone)]
110pub struct ServerConfiguration {
111 pub entries: HashMap<String, ServerConfigurationEntries>,
113
114 pub filters: ServerConfigurationFilters,
116
117 pub modules: Vec<Arc<dyn Module + Send + Sync>>,
119}
120
121impl Debug for ServerConfiguration {
122 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
123 f.debug_struct("ServerConfiguration")
124 .field("entries", &self.entries)
125 .field("filters", &self.filters)
126 .finish()
127 }
128}
129
130#[derive(Clone, Debug, PartialEq, Eq)]
132pub enum ErrorHandlerStatus {
133 Any,
135
136 Status(u16),
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct ServerConfigurationFilters {
143 pub is_host: bool,
145
146 pub hostname: Option<String>,
148
149 pub ip: Option<IpAddr>,
151
152 pub port: Option<u16>,
154
155 pub condition: Option<Conditions>,
157
158 pub error_handler_status: Option<ErrorHandlerStatus>,
160}
161
162impl ServerConfigurationFilters {
163 pub fn is_global(&self) -> bool {
165 self.is_host
166 && self.hostname.is_none()
167 && self.ip.is_none()
168 && self.port.is_none()
169 && self.condition.is_none()
170 && self.error_handler_status.is_none()
171 }
172
173 pub fn is_global_non_host(&self) -> bool {
175 !self.is_host
176 }
177}
178
179impl Ord for ServerConfigurationFilters {
180 fn cmp(&self, other: &Self) -> Ordering {
181 self
182 .is_host
183 .cmp(&other.is_host)
184 .then_with(|| self.port.is_some().cmp(&other.port.is_some()))
185 .then_with(|| self.ip.is_some().cmp(&other.ip.is_some()))
186 .then_with(|| {
187 self
188 .hostname
189 .as_ref()
190 .map(|h| !h.starts_with("*."))
191 .cmp(&other.hostname.as_ref().map(|h| !h.starts_with("*.")))
192 }) .then_with(|| {
194 self
195 .hostname
196 .as_ref()
197 .map(|h| h.trim_end_matches('.').chars().filter(|c| *c == '.').count())
198 .cmp(
199 &other
200 .hostname
201 .as_ref()
202 .map(|h| h.trim_end_matches('.').chars().filter(|c| *c == '.').count()),
203 )
204 }) .then_with(|| self.condition.cmp(&other.condition)) .then_with(|| {
207 self
208 .error_handler_status
209 .is_some()
210 .cmp(&other.error_handler_status.is_some())
211 })
212 }
213}
214
215impl PartialOrd for ServerConfigurationFilters {
216 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
217 Some(self.cmp(other))
218 }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq, Hash)]
223pub struct ServerConfigurationEntries {
224 pub inner: Vec<ServerConfigurationEntry>,
226}
227
228impl ServerConfigurationEntries {
229 pub fn get_value(&self) -> Option<&ServerConfigurationValue> {
231 self.inner.last().and_then(|last_vector| last_vector.values.first())
232 }
233
234 pub fn get_entry(&self) -> Option<&ServerConfigurationEntry> {
236 self.inner.last()
237 }
238
239 pub fn get_values(&self) -> Vec<&ServerConfigurationValue> {
241 let mut iterator: Box<dyn Iterator<Item = &ServerConfigurationValue>> = Box::new(vec![].into_iter());
242 for entry in &self.inner {
243 iterator = Box::new(iterator.chain(entry.values.iter()));
244 }
245 iterator.collect()
246 }
247}
248
249#[derive(Debug, Clone, PartialEq, Eq)]
251pub struct ServerConfigurationEntry {
252 pub values: Vec<ServerConfigurationValue>,
254
255 pub props: HashMap<String, ServerConfigurationValue>,
257}
258
259impl std::hash::Hash for ServerConfigurationEntry {
260 fn hash<H: Hasher>(&self, state: &mut H) {
261 self.values.hash(state);
263
264 let mut props_vec: Vec<_> = self.props.iter().collect();
267 props_vec.sort_by(|a, b| a.0.cmp(b.0)); props_vec.len().hash(state);
271 for (key, value) in props_vec {
272 key.hash(state);
273 value.hash(state);
274 }
275 }
276}
277
278#[derive(Debug, Clone, PartialOrd)]
280pub enum ServerConfigurationValue {
281 String(String),
283
284 Integer(i128),
286
287 Float(f64),
289
290 Bool(bool),
292
293 Null,
295}
296
297impl std::hash::Hash for ServerConfigurationValue {
298 fn hash<H: Hasher>(&self, state: &mut H) {
299 match self {
300 Self::String(s) => {
301 0u8.hash(state);
302 s.hash(state);
303 }
304 Self::Integer(i) => {
305 1u8.hash(state);
306 i.hash(state);
307 }
308 Self::Float(f) => {
309 2u8.hash(state);
310 if f.is_nan() {
313 f64::NAN.to_bits().hash(state);
314 } else {
315 f.to_bits().hash(state);
316 }
317 }
318 Self::Bool(b) => {
319 3u8.hash(state);
320 b.hash(state);
321 }
322 Self::Null => {
323 4u8.hash(state);
324 }
325 }
326 }
327}
328
329impl ServerConfigurationValue {
330 pub fn is_string(&self) -> bool {
332 matches!(self, Self::String(..))
333 }
334
335 pub fn is_integer(&self) -> bool {
337 matches!(self, Self::Integer(..))
338 }
339
340 #[allow(dead_code)]
342 pub fn is_float(&self) -> bool {
343 matches!(self, Self::Float(..))
344 }
345
346 pub fn is_bool(&self) -> bool {
348 matches!(self, Self::Bool(..))
349 }
350
351 pub fn is_null(&self) -> bool {
353 matches!(self, Self::Null)
354 }
355
356 pub fn as_str(&self) -> Option<&str> {
358 use ServerConfigurationValue::*;
359 match self {
360 String(s) => Some(s),
361 _ => None,
362 }
363 }
364
365 pub fn as_i128(&self) -> Option<i128> {
367 use ServerConfigurationValue::*;
368 match self {
369 Integer(i) => Some(*i),
370 _ => None,
371 }
372 }
373
374 #[allow(dead_code)]
376 pub fn as_f64(&self) -> Option<f64> {
377 match self {
378 Self::Float(i) => Some(*i),
379 _ => None,
380 }
381 }
382
383 pub fn as_bool(&self) -> Option<bool> {
385 if let Self::Bool(v) = self {
386 Some(*v)
387 } else {
388 None
389 }
390 }
391}
392
393impl Eq for ServerConfigurationValue {}
394
395impl PartialEq for ServerConfigurationValue {
396 fn eq(&self, other: &Self) -> bool {
397 match (self, other) {
398 (Self::Bool(left), Self::Bool(right)) => left == right,
399 (Self::Integer(left), Self::Integer(right)) => left == right,
400 (Self::Float(left), Self::Float(right)) => {
401 let left = if left == &f64::NEG_INFINITY {
402 -f64::MAX
403 } else if left == &f64::INFINITY {
404 f64::MAX
405 } else if left.is_nan() {
406 0.0
407 } else {
408 *left
409 };
410
411 let right = if right == &f64::NEG_INFINITY {
412 -f64::MAX
413 } else if right == &f64::INFINITY {
414 f64::MAX
415 } else if right.is_nan() {
416 0.0
417 } else {
418 *right
419 };
420
421 left == right
422 }
423 (Self::String(left), Self::String(right)) => left == right,
424 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
425 }
426 }
427}