1use std::fmt::{Debug, Display, 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::observability::ObservabilityBackendChannels;
11use crate::util::IpBlockList;
12
13#[non_exhaustive]
15#[repr(u8)]
16#[derive(Clone, Debug)]
17pub enum ConditionalData {
18 IsRemoteIp(IpBlockList),
19 IsForwardedFor(IpBlockList),
20 IsNotRemoteIp(IpBlockList),
21 IsNotForwardedFor(IpBlockList),
22 IsEqual(String, String),
23 IsNotEqual(String, String),
24 IsRegex(String, Regex),
25 IsNotRegex(String, Regex),
26 IsRego(Arc<regorus::Engine>),
27 SetConstant(String, String),
28 IsLanguage(String),
29}
30
31impl PartialEq for ConditionalData {
32 fn eq(&self, other: &Self) -> bool {
33 match (self, other) {
34 (Self::IsRemoteIp(v1), Self::IsRemoteIp(v2)) => v1 == v2,
35 (Self::IsForwardedFor(v1), Self::IsForwardedFor(v2)) => v1 == v2,
36 (Self::IsNotRemoteIp(v1), Self::IsNotRemoteIp(v2)) => v1 == v2,
37 (Self::IsNotForwardedFor(v1), Self::IsNotForwardedFor(v2)) => v1 == v2,
38 (Self::IsEqual(v1, v2), Self::IsEqual(v3, v4)) => v1 == v3 && v2 == v4,
39 (Self::IsNotEqual(v1, v2), Self::IsNotEqual(v3, v4)) => v1 == v3 && v2 == v4,
40 (Self::IsRegex(v1, v2), Self::IsRegex(v3, v4)) => v1 == v3 && v2.as_str() == v4.as_str(),
41 (Self::IsNotRegex(v1, v2), Self::IsNotRegex(v3, v4)) => v1 == v3 && v2.as_str() == v4.as_str(),
42 (Self::IsRego(v1), Self::IsRego(v2)) => v1.get_policies().ok() == v2.get_policies().ok(),
43 (Self::SetConstant(v1, v2), Self::SetConstant(v3, v4)) => v1 == v3 && v2 == v4,
44 (Self::IsLanguage(v1), Self::IsLanguage(v2)) => v1 == v2,
45 _ => false,
46 }
47 }
48}
49
50impl Eq for ConditionalData {}
51
52impl Ord for ConditionalData {
53 fn cmp(&self, other: &Self) -> Ordering {
54 match (self, other) {
55 (Self::IsRemoteIp(v1), Self::IsRemoteIp(v2)) => v1.cmp(v2),
56 (Self::IsForwardedFor(v1), Self::IsForwardedFor(v2)) => v1.cmp(v2),
57 (Self::IsNotRemoteIp(v1), Self::IsNotRemoteIp(v2)) => v1.cmp(v2),
58 (Self::IsNotForwardedFor(v1), Self::IsNotForwardedFor(v2)) => v1.cmp(v2),
59 (Self::IsEqual(v1, v2), Self::IsEqual(v3, v4)) => v1.cmp(v3).then(v2.cmp(v4)),
60 (Self::IsNotEqual(v1, v2), Self::IsNotEqual(v3, v4)) => v1.cmp(v3).then(v2.cmp(v4)),
61 (Self::IsRegex(v1, v2), Self::IsRegex(v3, v4)) => v1.cmp(v3).then(v2.as_str().cmp(v4.as_str())),
62 (Self::IsNotRegex(v1, v2), Self::IsNotRegex(v3, v4)) => v1.cmp(v3).then(v2.as_str().cmp(v4.as_str())),
63 (Self::IsRego(v1), Self::IsRego(v2)) => v1.get_policies().ok().cmp(&v2.get_policies().ok()),
64 (Self::SetConstant(v1, v2), Self::SetConstant(v3, v4)) => v1.cmp(v3).then(v2.cmp(v4)),
65 _ => {
66 let discriminant_self = unsafe { *<*const _>::from(self).cast::<u8>() };
68 let discriminant_other = unsafe { *<*const _>::from(other).cast::<u8>() };
69 discriminant_self.cmp(&discriminant_other)
70 }
71 }
72 }
73}
74
75impl PartialOrd for ConditionalData {
76 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
77 Some(self.cmp(other))
78 }
79}
80
81fn count_logical_slashes(s: &str) -> usize {
82 if s.is_empty() {
83 return 0;
85 }
86 let trimmed = s.trim_end_matches('/');
87 if trimmed.is_empty() {
88 return 0;
90 }
91
92 let mut count = 0;
93 let mut prev_was_slash = false;
94
95 for ch in trimmed.chars() {
96 if ch == '/' {
97 if !prev_was_slash {
98 count += 1;
99 prev_was_slash = true;
100 }
101 } else {
102 prev_was_slash = false;
103 }
104 }
105
106 count
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
111pub struct Conditions {
112 pub location_prefix: String,
114
115 pub conditionals: Vec<Conditional>,
117}
118
119impl Ord for Conditions {
120 fn cmp(&self, other: &Self) -> Ordering {
121 count_logical_slashes(&self.location_prefix)
122 .cmp(&count_logical_slashes(&other.location_prefix))
123 .then_with(|| self.conditionals.len().cmp(&other.conditionals.len()))
124 }
125}
126
127impl PartialOrd for Conditions {
128 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
129 Some(self.cmp(other))
130 }
131}
132
133#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
135pub enum Conditional {
136 If(Vec<ConditionalData>),
138
139 IfNot(Vec<ConditionalData>),
141}
142
143#[derive(Clone)]
145pub struct ServerConfiguration {
146 pub entries: HashMap<String, ServerConfigurationEntries>,
148
149 pub filters: ServerConfigurationFilters,
151
152 pub modules: Vec<Arc<dyn Module + Send + Sync>>,
154
155 pub observability: ObservabilityBackendChannels,
157}
158
159impl Debug for ServerConfiguration {
160 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
161 f.debug_struct("ServerConfiguration")
162 .field("entries", &self.entries)
163 .field("filters", &self.filters)
164 .finish()
165 }
166}
167
168#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
170pub enum ErrorHandlerStatus {
171 Any,
173
174 Status(u16),
176}
177
178#[derive(Clone, Debug, PartialEq, Eq)]
180pub struct ServerConfigurationFilters {
181 pub is_host: bool,
183
184 pub hostname: Option<String>,
186
187 pub ip: Option<IpAddr>,
189
190 pub port: Option<u16>,
192
193 pub condition: Option<Conditions>,
195
196 pub error_handler_status: Option<ErrorHandlerStatus>,
198}
199
200impl ServerConfigurationFilters {
201 pub fn is_global(&self) -> bool {
203 self.is_host
204 && self.hostname.is_none()
205 && self.ip.is_none()
206 && self.port.is_none()
207 && self.condition.is_none()
208 && self.error_handler_status.is_none()
209 }
210
211 pub fn is_global_non_host(&self) -> bool {
213 !self.is_host
214 }
215}
216
217impl Ord for ServerConfigurationFilters {
218 fn cmp(&self, other: &Self) -> Ordering {
219 self
220 .is_host
221 .cmp(&other.is_host)
222 .then_with(|| self.port.is_some().cmp(&other.port.is_some()))
223 .then_with(|| self.ip.is_some().cmp(&other.ip.is_some()))
224 .then_with(|| {
225 self
226 .hostname
227 .as_ref()
228 .map(|h| !h.starts_with("*."))
229 .cmp(&other.hostname.as_ref().map(|h| !h.starts_with("*.")))
230 }) .then_with(|| {
232 self
233 .hostname
234 .as_ref()
235 .map(|h| h.trim_end_matches('.').chars().filter(|c| *c == '.').count())
236 .cmp(
237 &other
238 .hostname
239 .as_ref()
240 .map(|h| h.trim_end_matches('.').chars().filter(|c| *c == '.').count()),
241 )
242 }) .then_with(|| self.condition.cmp(&other.condition)) .then_with(|| {
245 self
246 .error_handler_status
247 .is_some()
248 .cmp(&other.error_handler_status.is_some())
249 })
250 }
251}
252
253impl PartialOrd for ServerConfigurationFilters {
254 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
255 Some(self.cmp(other))
256 }
257}
258
259impl Display for ServerConfigurationFilters {
260 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
261 if !self.is_host {
262 write!(f, "\"globals\" block")
263 } else {
264 let mut blocks = Vec::new();
265 if self.ip.is_some() || self.hostname.is_some() || self.port.is_some() {
266 let mut name = String::new();
267 if let Some(hostname) = &self.hostname {
268 name.push_str(hostname);
269 if let Some(ip) = self.ip {
270 name.push_str(&format!("({ip})"));
271 }
272 } else if let Some(ip) = self.ip {
273 name.push_str(&ip.to_string());
274 }
275 if let Some(port) = self.port {
276 name.push_str(&format!(":{port}"));
277 }
278 if !name.is_empty() {
279 blocks.push(format!("\"{name}\" host block",));
280 }
281 }
282 if let Some(condition) = &self.condition {
283 let mut name = String::new();
284 if !condition.location_prefix.is_empty() {
285 name.push_str(&format!("\"{}\" location", condition.location_prefix));
286 }
287 if !condition.conditionals.is_empty() {
288 if !name.is_empty() {
289 name.push_str(" and ");
290 }
291 name.push_str("some conditional blocks");
292 } else {
293 name.push_str(" block");
294 }
295 blocks.push(name);
296 }
297 if let Some(error_handler_status) = &self.error_handler_status {
298 match error_handler_status {
299 ErrorHandlerStatus::Any => blocks.push("\"error_status\" block".to_string()),
300 ErrorHandlerStatus::Status(status) => blocks.push(format!("\"error_status {status}\" block")),
301 }
302 }
303 write!(f, "{}", blocks.join(" -> "))
304 }
305 }
306}
307
308#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
310pub struct ServerConfigurationEntries {
311 pub inner: Vec<ServerConfigurationEntry>,
313}
314
315impl ServerConfigurationEntries {
316 pub fn get_value(&self) -> Option<&ServerConfigurationValue> {
318 self.inner.last().and_then(|last_vector| last_vector.values.first())
319 }
320
321 pub fn get_entry(&self) -> Option<&ServerConfigurationEntry> {
323 self.inner.last()
324 }
325
326 pub fn get_values(&self) -> Vec<&ServerConfigurationValue> {
328 let mut iterator: Box<dyn Iterator<Item = &ServerConfigurationValue>> = Box::new(vec![].into_iter());
329 for entry in &self.inner {
330 iterator = Box::new(iterator.chain(entry.values.iter()));
331 }
332 iterator.collect()
333 }
334}
335
336#[derive(Debug, Clone, PartialEq, Eq)]
338pub struct ServerConfigurationEntry {
339 pub values: Vec<ServerConfigurationValue>,
341
342 pub props: HashMap<String, ServerConfigurationValue>,
344}
345
346impl std::hash::Hash for ServerConfigurationEntry {
347 fn hash<H: Hasher>(&self, state: &mut H) {
348 self.values.hash(state);
350
351 let mut props_vec: Vec<_> = self.props.iter().collect();
354 props_vec.sort_by(|a, b| a.0.cmp(b.0)); props_vec.len().hash(state);
358 for (key, value) in props_vec {
359 key.hash(state);
360 value.hash(state);
361 }
362 }
363}
364
365#[derive(Debug, Clone, PartialOrd)]
367pub enum ServerConfigurationValue {
368 String(String),
370
371 Integer(i128),
373
374 Float(f64),
376
377 Bool(bool),
379
380 Null,
382}
383
384impl std::hash::Hash for ServerConfigurationValue {
385 fn hash<H: Hasher>(&self, state: &mut H) {
386 match self {
387 Self::String(s) => {
388 0u8.hash(state);
389 s.hash(state);
390 }
391 Self::Integer(i) => {
392 1u8.hash(state);
393 i.hash(state);
394 }
395 Self::Float(f) => {
396 2u8.hash(state);
397 if f.is_nan() {
400 f64::NAN.to_bits().hash(state);
401 } else {
402 f.to_bits().hash(state);
403 }
404 }
405 Self::Bool(b) => {
406 3u8.hash(state);
407 b.hash(state);
408 }
409 Self::Null => {
410 4u8.hash(state);
411 }
412 }
413 }
414}
415
416impl ServerConfigurationValue {
417 pub fn is_string(&self) -> bool {
419 matches!(self, Self::String(..))
420 }
421
422 pub fn is_integer(&self) -> bool {
424 matches!(self, Self::Integer(..))
425 }
426
427 #[allow(dead_code)]
429 pub fn is_float(&self) -> bool {
430 matches!(self, Self::Float(..))
431 }
432
433 pub fn is_bool(&self) -> bool {
435 matches!(self, Self::Bool(..))
436 }
437
438 pub fn is_null(&self) -> bool {
440 matches!(self, Self::Null)
441 }
442
443 pub fn as_str(&self) -> Option<&str> {
445 use ServerConfigurationValue::*;
446 match self {
447 String(s) => Some(s),
448 _ => None,
449 }
450 }
451
452 pub fn as_i128(&self) -> Option<i128> {
454 use ServerConfigurationValue::*;
455 match self {
456 Integer(i) => Some(*i),
457 _ => None,
458 }
459 }
460
461 #[allow(dead_code)]
463 pub fn as_f64(&self) -> Option<f64> {
464 match self {
465 Self::Float(i) => Some(*i),
466 _ => None,
467 }
468 }
469
470 pub fn as_bool(&self) -> Option<bool> {
472 if let Self::Bool(v) = self {
473 Some(*v)
474 } else {
475 None
476 }
477 }
478}
479
480impl Eq for ServerConfigurationValue {}
481
482impl PartialEq for ServerConfigurationValue {
483 fn eq(&self, other: &Self) -> bool {
484 match (self, other) {
485 (Self::Bool(left), Self::Bool(right)) => left == right,
486 (Self::Integer(left), Self::Integer(right)) => left == right,
487 (Self::Float(left), Self::Float(right)) => {
488 let left = if left == &f64::NEG_INFINITY {
489 -f64::MAX
490 } else if left == &f64::INFINITY {
491 f64::MAX
492 } else if left.is_nan() {
493 0.0
494 } else {
495 *left
496 };
497
498 let right = if right == &f64::NEG_INFINITY {
499 -f64::MAX
500 } else if right == &f64::INFINITY {
501 f64::MAX
502 } else if right.is_nan() {
503 0.0
504 } else {
505 *right
506 };
507
508 left == right
509 }
510 (Self::String(left), Self::String(right)) => left == right,
511 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
512 }
513 }
514}