1use std::{
2 collections::{HashMap, HashSet},
3 error::Error,
4};
5
6use ferron_common::modules::ModuleLoader;
7
8use super::{ServerConfiguration, ServerConfigurationFilters};
9
10pub fn merge_duplicates(mut server_configurations: Vec<ServerConfiguration>) -> Vec<ServerConfiguration> {
17 let mut server_configurations_without_duplicates = Vec::new();
19
20 while !server_configurations.is_empty() {
22 let mut server_configuration = server_configurations.remove(0);
24 let mut server_configurations_index = 0;
25
26 while server_configurations_index < server_configurations.len() {
28 let server_configuration_source = &server_configurations[server_configurations_index];
30
31 if server_configuration_source.filters.is_host == server_configuration.filters.is_host
33 && server_configuration_source.filters.hostname == server_configuration.filters.hostname
34 && server_configuration_source.filters.ip == server_configuration.filters.ip
35 && server_configuration_source.filters.port == server_configuration.filters.port
36 && server_configuration_source.filters.condition == server_configuration.filters.condition
37 && server_configuration_source.filters.error_handler_status == server_configuration.filters.error_handler_status
38 {
39 let mut cloned_hashmap = server_configuration_source.entries.clone();
41 let moved_hashmap_iterator = server_configuration.entries.into_iter();
42
43 for (property_name, mut property) in moved_hashmap_iterator {
45 match cloned_hashmap.get_mut(&property_name) {
46 Some(obtained_property) => {
47 obtained_property.inner.append(&mut property.inner);
49 }
50 None => {
51 cloned_hashmap.insert(property_name, property);
53 }
54 }
55 }
56
57 server_configuration.entries = cloned_hashmap;
59
60 server_configurations.remove(server_configurations_index);
62 } else {
63 server_configurations_index += 1;
65 }
66 }
67
68 server_configurations_without_duplicates.push(server_configuration);
70 }
71
72 server_configurations_without_duplicates
74}
75
76pub fn remove_and_add_global_configuration(
82 server_configurations: Vec<ServerConfiguration>,
83) -> Vec<ServerConfiguration> {
84 let mut new_server_configurations = Vec::new();
86 let mut has_global_non_host = false;
88
89 for server_configuration in server_configurations {
91 if !server_configuration.entries.is_empty() {
93 if server_configuration.filters.is_global_non_host() {
95 has_global_non_host = true;
96 }
97 new_server_configurations.push(server_configuration);
99 }
100 }
101
102 if !has_global_non_host {
104 new_server_configurations.insert(
105 0,
106 ServerConfiguration {
107 entries: HashMap::new(),
108 filters: ServerConfigurationFilters {
109 is_host: false,
110 hostname: None,
111 ip: None,
112 port: None,
113 condition: None,
114 error_handler_status: None,
115 },
116 modules: vec![],
117 },
118 );
119 }
120
121 new_server_configurations
123}
124
125pub fn premerge_configuration(mut server_configurations: Vec<ServerConfiguration>) -> Vec<ServerConfiguration> {
132 server_configurations.sort_by(|a, b| a.filters.cmp(&b.filters));
134 let mut new_server_configurations = Vec::new();
135
136 while let Some(mut server_configuration) = server_configurations.pop() {
138 let mut layers_indexes = Vec::new();
140 for sc2_index in (0..server_configurations.len()).rev() {
142 let sc1 = &server_configuration.filters;
145 let sc2 = &server_configurations[sc2_index].filters;
146
147 let is_host_match = !sc2.is_host || sc1.is_host == sc2.is_host;
150 let ports_match = sc2.port.is_none() || sc1.port == sc2.port;
151 let ips_match = sc2.ip.is_none() || sc1.ip == sc2.ip;
152 let hostnames_match = sc2.hostname.is_none() || sc1.hostname == sc2.hostname;
153 let conditions_match = sc2.condition.is_none() || sc1.condition == sc2.condition;
154
155 let case1 = sc1.error_handler_status.is_some()
158 && sc2.error_handler_status.is_none()
159 && conditions_match
160 && hostnames_match
161 && ips_match
162 && ports_match
163 && is_host_match;
164
165 let case2 = sc1.error_handler_status.is_none()
168 && sc2.error_handler_status.is_none()
169 && sc1.condition.is_some()
170 && sc2.condition.is_none()
171 && hostnames_match
172 && ips_match
173 && ports_match
174 && is_host_match;
175
176 let case3 = sc1.error_handler_status.is_none()
179 && sc2.error_handler_status.is_none()
180 && sc1.condition.is_none()
181 && sc2.condition.is_none()
182 && sc1.hostname.is_some()
183 && sc2.hostname.is_none()
184 && ips_match
185 && ports_match
186 && is_host_match;
187
188 let case4 = sc1.error_handler_status.is_none()
191 && sc2.error_handler_status.is_none()
192 && sc1.condition.is_none()
193 && sc2.condition.is_none()
194 && sc1.hostname.is_none()
195 && sc2.hostname.is_none()
196 && sc1.ip.is_some()
197 && sc2.ip.is_none()
198 && ports_match
199 && is_host_match;
200
201 let case5 = sc1.error_handler_status.is_none()
204 && sc2.error_handler_status.is_none()
205 && sc1.condition.is_none()
206 && sc2.condition.is_none()
207 && sc1.hostname.is_none()
208 && sc2.hostname.is_none()
209 && sc1.ip.is_none()
210 && sc2.ip.is_none()
211 && sc1.port.is_some()
212 && sc2.port.is_none()
213 && is_host_match;
214
215 let case6 = sc1.error_handler_status.is_none()
218 && sc2.error_handler_status.is_none()
219 && sc1.condition.is_none()
220 && sc2.condition.is_none()
221 && sc1.hostname.is_none()
222 && sc2.hostname.is_none()
223 && sc1.ip.is_none()
224 && sc2.ip.is_none()
225 && sc1.port.is_none()
226 && sc2.port.is_none()
227 && sc1.is_host
228 && !sc2.is_host;
229
230 if case1 || case2 || case3 || case4 || case5 || case6 {
232 layers_indexes.push(sc2_index);
233 }
234 }
235
236 let mut configuration_entries = server_configuration.entries;
238
239 for layer_index in layers_indexes {
241 let mut properties_in_layer = HashSet::new();
243 let mut cloned_hashmap = server_configurations[layer_index].entries.clone();
245 let moved_hashmap_iterator = configuration_entries.into_iter();
247 for (property_name, mut property) in moved_hashmap_iterator {
249 match cloned_hashmap.get_mut(&property_name) {
250 Some(obtained_property) => {
251 if properties_in_layer.contains(&property_name) {
252 obtained_property.inner.append(&mut property.inner);
254 } else {
255 obtained_property.inner = property.inner;
257 }
258 }
259 None => {
260 cloned_hashmap.insert(property_name.clone(), property);
262 }
263 }
264 properties_in_layer.insert(property_name);
266 }
267 configuration_entries = cloned_hashmap;
269 }
270 server_configuration.entries = configuration_entries;
272
273 new_server_configurations.push(server_configuration);
275 }
276
277 new_server_configurations.reverse();
279 new_server_configurations
280}
281
282pub fn load_modules(
289 server_configurations: Vec<ServerConfiguration>,
290 server_modules: &mut [Box<dyn ModuleLoader + Send + Sync>],
291 secondary_runtime: &tokio::runtime::Runtime,
292) -> (
293 Vec<ServerConfiguration>,
294 Option<Box<dyn Error + Send + Sync>>,
295 Vec<String>,
296) {
297 let mut new_server_configurations = Vec::new();
299 let mut first_server_module_error = None;
301 let mut unused_properties = HashSet::new();
303
304 let global_configuration = find_global_configuration(&server_configurations);
306
307 for mut server_configuration in server_configurations {
309 let mut used_properties = HashSet::new();
311
312 for server_module in server_modules.iter_mut() {
314 let requirements = server_module.get_requirements();
316 let mut requirements_met = true;
318 for requirement in requirements {
319 requirements_met = false;
320 if server_configuration
322 .entries
323 .get(requirement)
324 .and_then(|e| e.get_value())
325 .is_some_and(|v| !v.is_null() && v.as_bool().unwrap_or(true))
326 {
327 requirements_met = true;
328 break;
329 }
330 }
331 match server_module.validate_configuration(&server_configuration, &mut used_properties) {
333 Ok(_) => (),
334 Err(error) => {
335 if first_server_module_error.is_none() {
337 first_server_module_error.replace(error);
338 }
339 break;
341 }
342 }
343 if requirements_met {
345 match server_module.load_module(&server_configuration, global_configuration.as_ref(), secondary_runtime) {
347 Ok(loaded_module) => server_configuration.modules.push(loaded_module),
348 Err(error) => {
349 if first_server_module_error.is_none() {
351 first_server_module_error.replace(error);
352 }
353 break;
355 }
356 }
357 }
358 }
359
360 for property in server_configuration.entries.keys() {
362 if !property.starts_with("UNDOCUMENTED_") && !used_properties.contains(property) {
363 unused_properties.insert(property.to_string());
364 }
365 }
366
367 new_server_configurations.push(server_configuration);
369 }
370 (
375 new_server_configurations,
376 first_server_module_error,
377 unused_properties.into_iter().collect(),
378 )
379}
380
381fn find_global_configuration(server_configurations: &[ServerConfiguration]) -> Option<ServerConfiguration> {
383 let mut iterator = server_configurations.iter();
385 let first_found = iterator.find(|server_configuration| {
386 server_configuration.filters.is_global() || server_configuration.filters.is_global_non_host()
387 });
388 if let Some(first_found) = first_found {
389 if first_found.filters.is_global() {
390 return Some(first_found.clone());
391 }
392 for server_configuration in iterator {
393 if server_configuration.filters.is_global() {
394 return Some(server_configuration.clone());
395 } else if !server_configuration.filters.is_global_non_host() {
396 return Some(first_found.clone());
397 }
398 }
399 }
400 None
401}
402
403#[cfg(test)]
404mod tests {
405 use crate::config::*;
406
407 use super::*;
408 use std::collections::HashMap;
409 use std::net::{IpAddr, Ipv4Addr};
410
411 fn make_filters(
412 is_host: bool,
413 hostname: Option<&str>,
414 ip: Option<IpAddr>,
415 port: Option<u16>,
416 location_prefix: Option<&str>,
417 error_handler_status: Option<ErrorHandlerStatus>,
418 ) -> ServerConfigurationFilters {
419 ServerConfigurationFilters {
420 is_host,
421 hostname: hostname.map(String::from),
422 ip,
423 port,
424 condition: location_prefix.map(|prefix| Conditions {
425 location_prefix: prefix.to_string(),
426 conditionals: vec![],
427 }),
428 error_handler_status,
429 }
430 }
431
432 fn make_entry(values: Vec<ServerConfigurationValue>) -> ServerConfigurationEntries {
433 ServerConfigurationEntries {
434 inner: vec![ServerConfigurationEntry {
435 values,
436 props: HashMap::new(),
437 }],
438 }
439 }
440
441 fn make_entry_premerge(key: &str, value: ServerConfigurationValue) -> (String, ServerConfigurationEntries) {
442 let entry = ServerConfigurationEntry {
443 values: vec![value],
444 props: HashMap::new(),
445 };
446 (key.to_string(), ServerConfigurationEntries { inner: vec![entry] })
447 }
448
449 fn config_with_filters(
450 is_host: bool,
451 hostname: Option<&str>,
452 ip: Option<IpAddr>,
453 port: Option<u16>,
454 location_prefix: Option<&str>,
455 error_handler_status: Option<ErrorHandlerStatus>,
456 entries: Vec<(String, ServerConfigurationEntries)>,
457 ) -> ServerConfiguration {
458 ServerConfiguration {
459 filters: ServerConfigurationFilters {
460 is_host,
461 hostname: hostname.map(|s| s.to_string()),
462 ip,
463 port,
464 condition: location_prefix.map(|prefix| Conditions {
465 location_prefix: prefix.to_string(),
466 conditionals: vec![],
467 }),
468 error_handler_status,
469 },
470 entries: entries.into_iter().collect(),
471 modules: vec![],
472 }
473 }
474
475 #[test]
476 fn merges_identical_filters_and_combines_entries() {
477 let filters = make_filters(
478 true,
479 Some("example.com"),
480 Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
481 Some(8080),
482 Some("/api"),
483 Some(ErrorHandlerStatus::Status(404)),
484 );
485
486 let filters_2 = make_filters(
487 true,
488 Some("example.com"),
489 Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
490 Some(8080),
491 Some("/api"),
492 Some(ErrorHandlerStatus::Status(404)),
493 );
494
495 let mut config1_entries = HashMap::new();
496 config1_entries.insert(
497 "route".to_string(),
498 make_entry(vec![ServerConfigurationValue::String("v1".to_string())]),
499 );
500
501 let mut config2_entries = HashMap::new();
502 config2_entries.insert(
503 "route".to_string(),
504 make_entry(vec![ServerConfigurationValue::String("v2".to_string())]),
505 );
506
507 let config1 = ServerConfiguration {
508 filters: filters_2,
509 entries: config1_entries,
510 modules: vec![],
511 };
512
513 let config2 = ServerConfiguration {
514 filters,
515 entries: config2_entries,
516 modules: vec![],
517 };
518
519 let merged = merge_duplicates(vec![config1, config2]);
520 assert_eq!(merged.len(), 1);
521
522 let merged_entries = &merged[0].entries;
523 assert!(merged_entries.contains_key("route"));
524 let route_entry = merged_entries.get("route").unwrap();
525 let values: Vec<_> = route_entry.inner.iter().flat_map(|e| e.values.iter()).collect();
526 assert_eq!(values.len(), 2);
527 assert!(values.contains(&&ServerConfigurationValue::String("v1".into())));
528 assert!(values.contains(&&ServerConfigurationValue::String("v2".into())));
529 }
530
531 #[test]
532 fn does_not_merge_different_filters() {
533 let filters1 = make_filters(
534 true,
535 Some("example.com"),
536 Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
537 Some(8080),
538 Some("/api"),
539 None,
540 );
541
542 let filters2 = make_filters(
543 true,
544 Some("example.org"),
545 Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
546 Some(8080),
547 Some("/api"),
548 None,
549 );
550
551 let mut config1_entries = HashMap::new();
552 config1_entries.insert(
553 "route".to_string(),
554 make_entry(vec![ServerConfigurationValue::String("v1".to_string())]),
555 );
556
557 let mut config2_entries = HashMap::new();
558 config2_entries.insert(
559 "route".to_string(),
560 make_entry(vec![ServerConfigurationValue::String("v2".to_string())]),
561 );
562
563 let config1 = ServerConfiguration {
564 filters: filters1,
565 entries: config1_entries,
566 modules: vec![],
567 };
568
569 let config2 = ServerConfiguration {
570 filters: filters2,
571 entries: config2_entries,
572 modules: vec![],
573 };
574
575 let merged = merge_duplicates(vec![config1, config2]);
576 assert_eq!(merged.len(), 2);
577 }
578
579 #[test]
580 fn merges_entries_with_non_overlapping_keys() {
581 let filters = make_filters(
582 true,
583 Some("example.com"),
584 Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
585 Some(8080),
586 None,
587 None,
588 );
589
590 let filters_2 = make_filters(
591 true,
592 Some("example.com"),
593 Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
594 Some(8080),
595 None,
596 None,
597 );
598
599 let mut config1_entries = HashMap::new();
600 config1_entries.insert(
601 "route1".to_string(),
602 make_entry(vec![ServerConfigurationValue::String("r1".to_string())]),
603 );
604
605 let mut config2_entries = HashMap::new();
606 config2_entries.insert(
607 "route2".to_string(),
608 make_entry(vec![ServerConfigurationValue::String("r2".to_string())]),
609 );
610
611 let config1 = ServerConfiguration {
612 filters: filters_2,
613 entries: config1_entries,
614 modules: vec![],
615 };
616
617 let config2 = ServerConfiguration {
618 filters,
619 entries: config2_entries,
620 modules: vec![],
621 };
622
623 let merged = merge_duplicates(vec![config1, config2]);
624 assert_eq!(merged.len(), 1);
625
626 let merged_entries = &merged[0].entries;
627 assert_eq!(merged_entries.len(), 2);
628 assert!(merged_entries.contains_key("route1"));
629 assert!(merged_entries.contains_key("route2"));
630 }
631
632 #[test]
633 fn test_no_merge_returns_all() {
634 let config1 = config_with_filters(
635 true,
636 Some("example.com"),
637 Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
638 Some(80),
639 None,
640 None,
641 vec![make_entry_premerge(
642 "key1",
643 ServerConfigurationValue::String("val1".into()),
644 )],
645 );
646
647 let config2 = config_with_filters(
648 true,
649 Some("example.org"),
650 Some(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1))),
651 Some(8080),
652 None,
653 None,
654 vec![make_entry_premerge(
655 "key2",
656 ServerConfigurationValue::String("val2".into()),
657 )],
658 );
659
660 let merged = premerge_configuration(vec![config1, config2]);
661
662 assert_eq!(merged.len(), 2);
663 assert!(merged.iter().any(|c| c.entries.contains_key("key1")));
664 assert!(merged.iter().any(|c| c.entries.contains_key("key2")));
665 }
666
667 #[test]
668 fn test_merge_case6_is_host() {
669 let base = config_with_filters(
671 false,
672 None,
673 None,
674 None,
675 None,
676 None,
677 vec![make_entry_premerge(
678 "shared",
679 ServerConfigurationValue::String("base".into()),
680 )],
681 );
682
683 let specific = config_with_filters(
685 true,
686 None,
687 None,
688 None,
689 None,
690 None,
691 vec![make_entry_premerge(
692 "shared",
693 ServerConfigurationValue::String("specific".into()),
694 )],
695 );
696
697 let merged = premerge_configuration(vec![base, specific]);
698 assert_eq!(merged.len(), 2);
699
700 let entries = &merged[1].entries["shared"].inner;
701 assert_eq!(entries.len(), 1);
702 assert_eq!(entries[0].values[0].as_str(), Some("specific"));
703 }
704
705 #[test]
706 fn test_merge_case5_port() {
707 let base = config_with_filters(
709 true,
710 None,
711 None,
712 None,
713 None,
714 None,
715 vec![make_entry_premerge(
716 "shared",
717 ServerConfigurationValue::String("base".into()),
718 )],
719 );
720
721 let specific = config_with_filters(
723 true,
724 None,
725 None,
726 Some(80),
727 None,
728 None,
729 vec![make_entry_premerge(
730 "shared",
731 ServerConfigurationValue::String("specific".into()),
732 )],
733 );
734
735 let merged = premerge_configuration(vec![base, specific]);
736 assert_eq!(merged.len(), 2);
737
738 let entries = &merged[1].entries["shared"].inner;
739 assert_eq!(entries.len(), 1);
740 assert_eq!(entries[0].values[0].as_str(), Some("specific"));
741 }
742
743 #[test]
744 fn test_merge_case1_error_handler() {
745 let base = config_with_filters(
746 true,
747 Some("host"),
748 Some(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))),
749 Some(3000),
750 Some("/api"),
751 None,
752 vec![make_entry_premerge(
753 "eh",
754 ServerConfigurationValue::String("base".into()),
755 )],
756 );
757
758 let specific = config_with_filters(
759 true,
760 Some("host"),
761 Some(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))),
762 Some(3000),
763 Some("/api"),
764 Some(ErrorHandlerStatus::Any),
765 vec![make_entry_premerge(
766 "eh",
767 ServerConfigurationValue::String("specific".into()),
768 )],
769 );
770
771 let merged = premerge_configuration(vec![base, specific]);
772 assert_eq!(merged.len(), 2);
773
774 let entries = &merged[1].entries["eh"].inner;
775 assert_eq!(entries.len(), 1);
776 assert_eq!(entries[0].values[0].as_str(), Some("specific"));
777 }
778
779 #[test]
780 fn test_merge_preserves_specificity_order() {
781 let configs = vec![
782 config_with_filters(
783 true,
784 None,
785 None,
786 None,
787 None,
788 None,
789 vec![make_entry_premerge("a", ServerConfigurationValue::String("v1".into()))],
790 ),
791 config_with_filters(
792 true,
793 None,
794 None,
795 Some(80),
796 None,
797 None,
798 vec![make_entry_premerge("a", ServerConfigurationValue::String("v2".into()))],
799 ),
800 config_with_filters(
801 true,
802 Some("host"),
803 None,
804 Some(80),
805 None,
806 None,
807 vec![make_entry_premerge("a", ServerConfigurationValue::String("v3".into()))],
808 ),
809 ];
810
811 let merged = premerge_configuration(configs);
812 assert_eq!(merged.len(), 3);
813
814 let entries = &merged[2].entries["a"].inner;
815 assert_eq!(entries.len(), 1);
816 assert_eq!(entries[0].values[0].as_str(), Some("v3"));
817 }
818}