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.map(|c| c.to_canonical()) {
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 }
105
106 pub fn get_default(&self) -> Option<&T> {
107 self.default_value.as_ref()
108 }
109
110 fn insert(&mut self, status_code: Option<u16>, value: T) {
111 if let Some(code) = status_code {
112 self.status_code_values.insert(code, value);
113 } else {
114 self.catchall_value = Some(value);
115 }
116 }
117
118 fn set_default(&mut self, value: T) {
119 self.default_value = Some(value);
120 }
121
122 pub fn has_status_codes(&self) -> bool {
123 self.catchall_value.is_some() || !self.status_code_values.is_empty()
124 }
125}
126
127#[derive(Debug)]
128pub struct ServerConfigurations {
129 inner: ConfigFilterTree<ErrorHandlerStatusLookupWithConfiguration>,
130
131 pub host_configs: Vec<Arc<ServerConfiguration>>,
134}
135
136impl ServerConfigurations {
137 pub fn new(mut inner: Vec<ServerConfiguration>) -> Self {
139 inner.reverse();
141
142 inner.sort_by(|a, b| a.filters.cmp(&b.filters));
146
147 let mut new_inner = ConfigFilterTree::new();
148 let mut host_config_filters = LinkedHashMap::new();
149
150 for config in inner {
151 let config = Arc::new(config);
152 if config.filters.is_host {
153 if config.filters.condition.is_none() && config.filters.error_handler_status.is_none() {
154 host_config_filters.insert(
155 (config.filters.hostname.clone(), config.filters.port, config.filters.ip),
156 config.clone(),
157 );
158 } else {
159 host_config_filters
160 .entry((config.filters.hostname.clone(), config.filters.port, config.filters.ip))
161 .or_insert_with(|| config.clone());
162 }
163 }
164 let error_handler_status = &config.filters.error_handler_status;
165 let node_key = convert_filters_to_node_key(&config.filters);
166 let parent_config_default_value = new_inner
167 .get(node_key, None)
168 .expect("configuration filter tree's get method shouldn't error out if no conditionals are checked against")
169 .and_then(|lookup: &ErrorHandlerStatusLookup<_>| lookup.get_default().cloned());
170 let node_key = convert_filters_to_node_key(&config.filters);
171 let value_option = new_inner.insert_node(node_key);
172 if value_option.is_none() {
173 let mut new_value = ErrorHandlerStatusLookup::new();
174 if let Some(parent_default) = parent_config_default_value {
175 new_value.set_default(parent_default);
176 }
177 *value_option = Some(new_value);
178 }
179 if let Some(value) = value_option.as_mut() {
180 match error_handler_status {
181 Some(ErrorHandlerStatus::Status(status_code)) => value.insert(Some(*status_code), config),
182 Some(ErrorHandlerStatus::Any) => value.insert(None, config),
183 None => value.set_default(config),
184 }
185 }
186 }
187
188 Self {
189 inner: new_inner,
190 host_configs: host_config_filters.into_iter().map(|(_, config)| config).collect(),
191 }
192 }
193
194 pub fn find_configuration(
196 &self,
197 request: &hyper::http::request::Parts,
198 hostname: Option<&str>,
199 socket_data: &SocketData,
200 ) -> Result<Option<&ErrorHandlerStatusLookupWithConfiguration>, Box<dyn std::error::Error + Send + Sync>> {
201 let mut node_key = Vec::new();
202 node_key.push(ConfigFilterTreeSingleKey::IsHostConfiguration);
203 node_key.push(ConfigFilterTreeSingleKey::Port(socket_data.local_addr.port()));
204 let local_ip = socket_data.local_addr.ip().to_canonical();
205 if local_ip.is_loopback() {
206 node_key.push(ConfigFilterTreeSingleKey::IsLocalhost);
207 } else {
208 match local_ip {
209 IpAddr::V4(ipv4) => {
210 for octet in ipv4.octets() {
211 node_key.push(ConfigFilterTreeSingleKey::IPv4Octet(octet));
212 }
213 }
214 IpAddr::V6(ipv6) => {
215 for octet in ipv6.octets() {
216 node_key.push(ConfigFilterTreeSingleKey::IPv6Octet(octet));
217 }
218 }
219 }
220 }
221 if let Some(hostname) = hostname {
222 for part in hostname.split('.').rev() {
223 if part.is_empty() {
224 continue;
225 }
226 node_key.push(ConfigFilterTreeSingleKey::HostDomainLevel(part.to_string()))
227 }
228 }
229 for part in request.uri.path().split("/") {
230 node_key.push(ConfigFilterTreeSingleKey::LocationSegment(part.to_string()));
231 }
232
233 self
234 .inner
235 .get(node_key, Some(ConditionMatchData { request, socket_data }))
236 }
237
238 pub fn find_global_configuration(&self) -> Option<Arc<ServerConfiguration>> {
240 self
241 .inner
242 .get(vec![ConfigFilterTreeSingleKey::IsHostConfiguration], None)
243 .expect("configuration filter tree's get method shouldn't error out if no conditionals are checked against")
244 .and_then(|lookup| lookup.get_default())
245 .cloned()
246 }
247}