ferron/config/lookup/
mod.rs1pub(super) mod conditionals;
2mod tree;
3
4use std::{collections::HashMap, net::IpAddr, sync::Arc};
5
6use ferron_common::{
7 config::{ErrorHandlerStatus, ServerConfiguration, ServerConfigurationFilters},
8 modules::SocketData,
9};
10use hashlink::LinkedHashMap;
11
12use crate::config::lookup::{
13 conditionals::ConditionMatchData,
14 tree::{ConfigFilterTree, ConfigFilterTreeSingleKey},
15};
16
17pub type ErrorHandlerStatusLookupWithConfiguration = ErrorHandlerStatusLookup<Arc<ServerConfiguration>>;
19
20#[inline]
22fn convert_filters_to_node_key(filters: &ServerConfigurationFilters) -> Vec<ConfigFilterTreeSingleKey> {
23 let mut node_key = Vec::new();
24 if filters.is_host {
25 node_key.push(ConfigFilterTreeSingleKey::IsHostConfiguration);
26
27 if let Some(port) = filters.port {
28 node_key.push(ConfigFilterTreeSingleKey::Port(port));
29 }
30
31 if let Some(ip) = filters.ip {
32 if ip.is_loopback() {
33 node_key.push(ConfigFilterTreeSingleKey::IsLocalhost);
34 } else {
35 match ip {
36 IpAddr::V4(ipv4) => {
37 for octet in ipv4.octets() {
38 node_key.push(ConfigFilterTreeSingleKey::IPv4Octet(octet));
39 }
40 }
41 IpAddr::V6(ipv6) => {
42 for octet in ipv6.octets() {
43 node_key.push(ConfigFilterTreeSingleKey::IPv6Octet(octet));
44 }
45 }
46 }
47 }
48 }
49
50 if let Some(hostname) = &filters.hostname {
51 for part in hostname.split('.').rev() {
52 if part.is_empty() {
53 continue;
54 }
55 match part {
56 "*" => node_key.push(ConfigFilterTreeSingleKey::HostDomainLevelWildcard),
57 _ => node_key.push(ConfigFilterTreeSingleKey::HostDomainLevel(part.to_string())),
58 }
59 }
60 }
61
62 if let Some(conditions) = &filters.condition {
63 let mut is_first = true;
64 for segment in conditions.location_prefix.split("/") {
65 if is_first || !segment.is_empty() {
66 node_key.push(ConfigFilterTreeSingleKey::LocationSegment(segment.to_string()));
67 }
68 is_first = false;
69 }
70 for conditional in &conditions.conditionals {
71 node_key.push(ConfigFilterTreeSingleKey::Conditional(conditional.clone()));
74 }
75 }
76 }
77
78 node_key
79}
80
81#[derive(Debug)]
84pub struct ErrorHandlerStatusLookup<T> {
85 default_value: Option<T>,
86 catchall_value: Option<T>,
87 status_code_values: HashMap<u16, T>,
88}
89
90impl<T> ErrorHandlerStatusLookup<T> {
91 fn new() -> Self {
92 Self {
93 default_value: None,
94 catchall_value: None,
95 status_code_values: HashMap::new(),
96 }
97 }
98
99 pub fn get(&self, status_code: u16) -> Option<&T> {
100 self
101 .status_code_values
102 .get(&status_code)
103 .or(self.catchall_value.as_ref())
104 .or(self.default_value.as_ref())
105 }
106
107 pub fn get_default(&self) -> Option<&T> {
108 self.default_value.as_ref()
109 }
110
111 fn insert(&mut self, status_code: Option<u16>, value: T) {
112 if let Some(code) = status_code {
113 self.status_code_values.insert(code, value);
114 } else {
115 self.catchall_value = Some(value);
116 }
117 }
118
119 fn set_default(&mut self, value: T) {
120 self.default_value = Some(value);
121 }
122
123 pub fn has_status_codes(&self) -> bool {
124 self.catchall_value.is_some() || !self.status_code_values.is_empty()
125 }
126}
127
128#[derive(Debug)]
129pub struct ServerConfigurations {
130 inner: ConfigFilterTree<ErrorHandlerStatusLookupWithConfiguration>,
131
132 pub host_configs: Vec<Arc<ServerConfiguration>>,
135}
136
137impl ServerConfigurations {
138 pub fn new(mut inner: Vec<ServerConfiguration>) -> Self {
140 inner.reverse();
142
143 inner.sort_by(|a, b| a.filters.cmp(&b.filters));
147
148 let mut new_inner = ConfigFilterTree::new();
149 let mut host_config_filters = LinkedHashMap::new();
150
151 for config in inner {
152 let config = Arc::new(config);
153 if config.filters.is_host {
154 if config.filters.condition.is_none() && config.filters.error_handler_status.is_none() {
155 host_config_filters.insert(
156 (config.filters.hostname.clone(), config.filters.port, config.filters.ip),
157 config.clone(),
158 );
159 } else {
160 host_config_filters
161 .entry((config.filters.hostname.clone(), config.filters.port, config.filters.ip))
162 .or_insert_with(|| config.clone());
163 }
164 }
165 let error_handler_status = &config.filters.error_handler_status;
166 let node_key = convert_filters_to_node_key(&config.filters);
167 let parent_config_default_value = new_inner
168 .get(node_key, None)
169 .expect("configuration filter tree's get method shouldn't error out if no conditionals are checked against")
170 .and_then(|lookup: &ErrorHandlerStatusLookup<_>| lookup.get_default().cloned());
171 let node_key = convert_filters_to_node_key(&config.filters);
172 let value_option = new_inner.insert_node(node_key);
173 if value_option.is_none() {
174 let mut new_value = ErrorHandlerStatusLookup::new();
175 if let Some(parent_default) = parent_config_default_value {
176 new_value.set_default(parent_default);
177 }
178 *value_option = Some(new_value);
179 }
180 if let Some(value) = value_option.as_mut() {
181 match error_handler_status {
182 Some(ErrorHandlerStatus::Status(status_code)) => value.insert(Some(*status_code), config),
183 Some(ErrorHandlerStatus::Any) => value.insert(None, config),
184 None => value.set_default(config),
185 }
186 }
187 }
188
189 Self {
190 inner: new_inner,
191 host_configs: host_config_filters.into_iter().map(|(_, config)| config).collect(),
192 }
193 }
194
195 pub fn find_configuration(
197 &self,
198 request: &hyper::http::request::Parts,
199 hostname: Option<&str>,
200 socket_data: &SocketData,
201 ) -> Result<Option<&ErrorHandlerStatusLookupWithConfiguration>, Box<dyn std::error::Error + Send + Sync>> {
202 let mut node_key = Vec::new();
203 node_key.push(ConfigFilterTreeSingleKey::IsHostConfiguration);
204 node_key.push(ConfigFilterTreeSingleKey::Port(socket_data.local_addr.port()));
205 let local_ip = socket_data.local_addr.ip();
206 if local_ip.is_loopback() {
207 node_key.push(ConfigFilterTreeSingleKey::IsLocalhost);
208 } else {
209 match local_ip {
210 IpAddr::V4(ipv4) => {
211 for octet in ipv4.octets() {
212 node_key.push(ConfigFilterTreeSingleKey::IPv4Octet(octet));
213 }
214 }
215 IpAddr::V6(ipv6) => {
216 for octet in ipv6.octets() {
217 node_key.push(ConfigFilterTreeSingleKey::IPv6Octet(octet));
218 }
219 }
220 }
221 }
222 if let Some(hostname) = hostname {
223 for part in hostname.split('.').rev() {
224 if part.is_empty() {
225 continue;
226 }
227 node_key.push(ConfigFilterTreeSingleKey::HostDomainLevel(part.to_string()))
228 }
229 }
230 for part in request.uri.path().split("/") {
231 node_key.push(ConfigFilterTreeSingleKey::LocationSegment(part.to_string()));
232 }
233
234 self
235 .inner
236 .get(node_key, Some(ConditionMatchData { request, socket_data }))
237 }
238
239 pub fn find_global_configuration(&self) -> Option<Arc<ServerConfiguration>> {
241 self
242 .inner
243 .get(vec![ConfigFilterTreeSingleKey::IsHostConfiguration], None)
244 .expect("configuration filter tree's get method shouldn't error out if no conditionals are checked against")
245 .and_then(|lookup| lookup.get_default())
246 .cloned()
247 }
248}