1use std::{
2 collections::HashMap,
3 error::Error,
4 net::{IpAddr, SocketAddr},
5 path::Path,
6};
7
8use ferron_common::observability::ObservabilityBackendChannels;
9use ferron_yaml2kdl_core::convert_yaml_to_kdl;
10use kdl::{KdlDocument, KdlNode, KdlValue};
11
12use crate::config::{
13 Conditions, ErrorHandlerStatus, ServerConfiguration, ServerConfigurationEntries, ServerConfigurationEntry,
14 ServerConfigurationFilters, ServerConfigurationValue,
15};
16
17use super::ConfigurationAdapter;
18
19fn kdl_node_to_configuration_entry(kdl_node: &KdlNode) -> ServerConfigurationEntry {
20 let mut values = Vec::new();
21 let mut props = HashMap::new();
22 for kdl_entry in kdl_node.iter() {
23 let value = match kdl_entry.value().to_owned() {
24 KdlValue::String(value) => ServerConfigurationValue::String(value),
25 KdlValue::Integer(value) => ServerConfigurationValue::Integer(value),
26 KdlValue::Float(value) => ServerConfigurationValue::Float(value),
27 KdlValue::Bool(value) => ServerConfigurationValue::Bool(value),
28 KdlValue::Null => ServerConfigurationValue::Null,
29 };
30 if let Some(prop_name) = kdl_entry.name() {
31 props.insert(prop_name.value().to_string(), value);
32 } else {
33 values.push(value);
34 }
35 }
36 if values.is_empty() {
37 values.push(ServerConfigurationValue::Bool(true));
39 }
40 ServerConfigurationEntry { values, props }
41}
42
43pub struct YamlLegacyConfigurationAdapter;
45
46impl YamlLegacyConfigurationAdapter {
47 pub fn new() -> Self {
49 Self
50 }
51}
52
53impl ConfigurationAdapter for YamlLegacyConfigurationAdapter {
54 fn load_configuration(&self, path: &Path) -> Result<Vec<ServerConfiguration>, Box<dyn Error + Send + Sync>> {
55 let kdl_document: KdlDocument = match convert_yaml_to_kdl(path.to_path_buf()) {
57 Ok(document) => document,
58 Err(err) => Err(anyhow::anyhow!(
59 "Failed to read and parse the server configuration file: {}",
60 err
61 ))?,
62 };
63
64 let mut configurations = Vec::new();
66
67 for kdl_node in kdl_document {
69 let global_name = kdl_node.name().value();
70 let children = kdl_node.children();
71 if let Some(children) = children {
72 for global_name in global_name.split(",") {
73 let (hostname, ip, port, is_host) = if global_name == "globals" {
74 (None, None, None, false)
75 } else if let Ok(socket_addr) = global_name.parse::<SocketAddr>() {
76 (None, Some(socket_addr.ip()), Some(socket_addr.port()), true)
77 } else if let Some((address, port_str)) = global_name.rsplit_once(':') {
78 if let Ok(port) = port_str.parse::<u16>() {
79 if let Ok(ip_address) = address
80 .strip_prefix('[')
81 .and_then(|s| s.strip_suffix(']'))
82 .unwrap_or(address)
83 .parse::<IpAddr>()
84 {
85 (None, Some(ip_address), Some(port), true)
86 } else if address == "*" || address.is_empty() {
87 (None, None, Some(port), true)
88 } else {
89 (Some(address.to_string()), None, Some(port), true)
90 }
91 } else if port_str == "*" {
92 if let Ok(ip_address) = address
93 .strip_prefix('[')
94 .and_then(|s| s.strip_suffix(']'))
95 .unwrap_or(address)
96 .parse::<IpAddr>()
97 {
98 (None, Some(ip_address), None, true)
99 } else if address == "*" || address.is_empty() {
100 (None, None, None, true)
101 } else {
102 (Some(address.to_string()), None, None, true)
103 }
104 } else {
105 Err(anyhow::anyhow!("Invalid host specifier"))?
106 }
107 } else if let Ok(ip_address) = global_name
108 .strip_prefix('[')
109 .and_then(|s| s.strip_suffix(']'))
110 .unwrap_or(global_name)
111 .parse::<IpAddr>()
112 {
113 (None, Some(ip_address), None, true)
114 } else if global_name == "*" || global_name.is_empty() {
115 (None, None, None, true)
116 } else {
117 (Some(global_name.to_string()), None, None, true)
118 };
119
120 let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
121 for kdl_node in children.nodes() {
122 let kdl_node_name = kdl_node.name().value();
123 let children = kdl_node.children();
124 if kdl_node_name == "location" {
125 let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
126 if let Some(children) = children {
127 if let Some(location) = kdl_node.entry(0) {
128 if let Some(location_str) = location.value().as_string() {
129 for kdl_node in children.nodes() {
130 let kdl_node_name = kdl_node.name().value();
131 let children = kdl_node.children();
132 if kdl_node_name == "error_config" {
133 let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
134 if let Some(children) = children {
135 if let Some(error_status_code) = kdl_node.entry(0) {
136 if let Some(error_status_code) = error_status_code.value().as_integer() {
137 for kdl_node in children.nodes() {
138 let kdl_node_name = kdl_node.name().value();
139 let value = kdl_node_to_configuration_entry(kdl_node);
140 if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
141 entries.inner.push(value);
142 } else {
143 configuration_entries.insert(
144 kdl_node_name.to_string(),
145 ServerConfigurationEntries { inner: vec![value] },
146 );
147 }
148 }
149 configurations.push(ServerConfiguration {
150 entries: configuration_entries,
151 filters: ServerConfigurationFilters {
152 is_host,
153 hostname: hostname.clone(),
154 ip,
155 port,
156 condition: Some(Conditions {
157 location_prefix: location_str.to_string(),
158 conditionals: vec![],
159 }),
160 error_handler_status: Some(ErrorHandlerStatus::Status(error_status_code as u16)),
161 },
162 modules: vec![],
163 observability: ObservabilityBackendChannels::new(),
164 });
165 } else {
166 Err(anyhow::anyhow!("Invalid error handler status code"))?
167 }
168 } else {
169 for kdl_node in children.nodes() {
170 let kdl_node_name = kdl_node.name().value();
171 let value = kdl_node_to_configuration_entry(kdl_node);
172 if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
173 entries.inner.push(value);
174 } else {
175 configuration_entries.insert(
176 kdl_node_name.to_string(),
177 ServerConfigurationEntries { inner: vec![value] },
178 );
179 }
180 }
181 configurations.push(ServerConfiguration {
182 entries: configuration_entries,
183 filters: ServerConfigurationFilters {
184 is_host,
185 hostname: hostname.clone(),
186 ip,
187 port,
188 condition: Some(Conditions {
189 location_prefix: location_str.to_string(),
190 conditionals: vec![],
191 }),
192 error_handler_status: Some(ErrorHandlerStatus::Any),
193 },
194 modules: vec![],
195 observability: ObservabilityBackendChannels::new(),
196 });
197 }
198 } else {
199 Err(anyhow::anyhow!(
200 "Error handler blocks should have children, but they don't"
201 ))?
202 }
203 } else {
204 let value = kdl_node_to_configuration_entry(kdl_node);
205 if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
206 entries.inner.push(value);
207 } else {
208 configuration_entries.insert(
209 kdl_node_name.to_string(),
210 ServerConfigurationEntries { inner: vec![value] },
211 );
212 }
213 }
214 }
215 if kdl_node
216 .entry("remove_base")
217 .and_then(|e| e.value().as_bool())
218 .unwrap_or(false)
219 {
220 configuration_entries.insert(
221 "UNDOCUMENTED_REMOVE_PATH_PREFIX".to_string(),
222 ServerConfigurationEntries {
223 inner: vec![ServerConfigurationEntry {
224 values: vec![ServerConfigurationValue::String(location_str.to_string())],
225 props: HashMap::new(),
226 }],
227 },
228 );
229 }
230 configurations.push(ServerConfiguration {
231 entries: configuration_entries,
232 filters: ServerConfigurationFilters {
233 is_host,
234 hostname: hostname.clone(),
235 ip,
236 port,
237 condition: Some(Conditions {
238 location_prefix: location_str.to_string(),
239 conditionals: vec![],
240 }),
241 error_handler_status: None,
242 },
243 modules: vec![],
244 observability: ObservabilityBackendChannels::new(),
245 });
246 } else {
247 Err(anyhow::anyhow!("Invalid location path"))?
248 }
249 } else {
250 Err(anyhow::anyhow!("Invalid location"))?
251 }
252 } else {
253 Err(anyhow::anyhow!("Locations should have children, but they don't"))?
254 }
255 } else if kdl_node_name == "error_config" {
256 let mut configuration_entries: HashMap<String, ServerConfigurationEntries> = HashMap::new();
257 if let Some(children) = children {
258 if let Some(error_status_code) = kdl_node.entry(0) {
259 if let Some(error_status_code) = error_status_code.value().as_integer() {
260 for kdl_node in children.nodes() {
261 let kdl_node_name = kdl_node.name().value();
262 let value = kdl_node_to_configuration_entry(kdl_node);
263 if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
264 entries.inner.push(value);
265 } else {
266 configuration_entries.insert(
267 kdl_node_name.to_string(),
268 ServerConfigurationEntries { inner: vec![value] },
269 );
270 }
271 }
272 configurations.push(ServerConfiguration {
273 entries: configuration_entries,
274 filters: ServerConfigurationFilters {
275 is_host,
276 hostname: hostname.clone(),
277 ip,
278 port,
279 condition: None,
280 error_handler_status: Some(ErrorHandlerStatus::Status(error_status_code as u16)),
281 },
282 modules: vec![],
283 observability: ObservabilityBackendChannels::new(),
284 });
285 } else {
286 Err(anyhow::anyhow!("Invalid error handler status code"))?
287 }
288 } else {
289 for kdl_node in children.nodes() {
290 let kdl_node_name = kdl_node.name().value();
291 let value = kdl_node_to_configuration_entry(kdl_node);
292 if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
293 entries.inner.push(value);
294 } else {
295 configuration_entries.insert(
296 kdl_node_name.to_string(),
297 ServerConfigurationEntries { inner: vec![value] },
298 );
299 }
300 }
301 configurations.push(ServerConfiguration {
302 entries: configuration_entries,
303 filters: ServerConfigurationFilters {
304 is_host,
305 hostname: hostname.clone(),
306 ip,
307 port,
308 condition: None,
309 error_handler_status: Some(ErrorHandlerStatus::Any),
310 },
311 modules: vec![],
312 observability: ObservabilityBackendChannels::new(),
313 });
314 }
315 } else {
316 Err(anyhow::anyhow!(
317 "Error handler blocks should have children, but they don't"
318 ))?
319 }
320 } else {
321 let value = kdl_node_to_configuration_entry(kdl_node);
322 if let Some(entries) = configuration_entries.get_mut(kdl_node_name) {
323 entries.inner.push(value);
324 } else {
325 configuration_entries.insert(
326 kdl_node_name.to_string(),
327 ServerConfigurationEntries { inner: vec![value] },
328 );
329 }
330 }
331 }
332 configurations.push(ServerConfiguration {
333 entries: configuration_entries,
334 filters: ServerConfigurationFilters {
335 is_host,
336 hostname,
337 ip,
338 port,
339 condition: None,
340 error_handler_status: None,
341 },
342 modules: vec![],
343 observability: ObservabilityBackendChannels::new(),
344 });
345 }
346 } else {
347 Err(anyhow::anyhow!("Invalid top-level directive"))?
349 }
350 }
351
352 Ok(configurations)
353 }
354}