ferron/
main.rs

1// Import server module from "server.rs"
2#[path = "server.rs"]
3mod ferron_server;
4
5// Import request handler module from "request_handler.rs"
6#[path = "request_handler.rs"]
7mod ferron_request_handler;
8
9// Import resources from "res" directory
10#[path = "res"]
11mod ferron_res {
12  pub mod server_software;
13}
14
15// Import common modules from "common" directory
16#[path = "common/mod.rs"]
17mod ferron_common;
18
19// Import utility modules from "util" directory
20#[path = "util"]
21mod ferron_util {
22  pub mod anti_xss;
23  #[cfg(feature = "asgi")]
24  pub mod asgi_messages;
25  #[cfg(any(feature = "cgi", feature = "scgi", feature = "fcgi"))]
26  pub mod cgi_response;
27  pub mod combine_config;
28  #[cfg(any(feature = "cgi", feature = "scgi", feature = "fcgi"))]
29  pub mod copy_move;
30  pub mod env_config;
31  pub mod error_config;
32  pub mod error_pages;
33  #[cfg(feature = "fcgi")]
34  pub mod fcgi_decoder;
35  #[cfg(feature = "fcgi")]
36  pub mod fcgi_encoder;
37  #[cfg(feature = "fcgi")]
38  pub mod fcgi_name_value_pair;
39  #[cfg(feature = "fcgi")]
40  pub mod fcgi_record;
41  pub mod generate_directory_listing;
42  pub mod ip_blocklist;
43  pub mod ip_match;
44  pub mod load_config;
45  pub mod load_tls;
46  pub mod match_hostname;
47  pub mod match_location;
48  #[cfg(any(feature = "rproxy", feature = "fauth"))]
49  pub mod no_server_verifier;
50  #[cfg(any(feature = "wsgi", feature = "wsgid", feature = "asgi"))]
51  pub mod obtain_config_struct;
52  pub mod obtain_config_struct_vec;
53  #[cfg(all(unix, feature = "wsgid"))]
54  pub mod preforked_process_pool;
55  #[cfg(feature = "fcgi")]
56  pub mod read_to_end_move;
57  pub mod sizify;
58  pub mod sni;
59  #[cfg(feature = "fcgi")]
60  pub mod split_stream_by_map;
61  pub mod ttl_cache;
62  pub mod url_sanitizer;
63  pub mod validate_config;
64  #[cfg(feature = "wsgi")]
65  pub mod wsgi_error_stream;
66  #[cfg(feature = "wsgi")]
67  pub mod wsgi_input_stream;
68  #[cfg(any(feature = "wsgi", feature = "wsgid"))]
69  pub mod wsgi_load_application;
70  #[cfg(feature = "wsgid")]
71  pub mod wsgid_body_reader;
72  #[cfg(feature = "wsgid")]
73  pub mod wsgid_error_stream;
74  #[cfg(feature = "wsgid")]
75  pub mod wsgid_input_stream;
76  #[cfg(feature = "wsgid")]
77  pub mod wsgid_message_structs;
78}
79
80// Import project modules from "modules" directory
81#[path = "modules"]
82mod ferron_modules {
83  pub mod blocklist;
84  pub mod default_handler_checks;
85  pub mod non_standard_codes;
86  pub mod redirect_trailing_slashes;
87  pub mod redirects;
88  pub mod static_file_serving;
89  pub mod url_rewrite;
90  pub mod x_forwarded_for;
91}
92
93// Import optional project modules from "modules" directory
94#[path = "optional_modules"]
95mod ferron_optional_modules {
96  #[cfg(feature = "asgi")]
97  pub mod asgi;
98  #[cfg(feature = "cache")]
99  pub mod cache;
100  #[cfg(feature = "cgi")]
101  pub mod cgi;
102  #[cfg(feature = "example")]
103  pub mod example;
104  #[cfg(feature = "fauth")]
105  pub mod fauth;
106  #[cfg(feature = "fcgi")]
107  pub mod fcgi;
108  #[cfg(feature = "fproxy")]
109  pub mod fproxy;
110  #[cfg(feature = "rproxy")]
111  pub mod rproxy;
112  #[cfg(feature = "scgi")]
113  pub mod scgi;
114  #[cfg(feature = "wsgi")]
115  pub mod wsgi;
116  #[cfg(feature = "wsgid")]
117  pub mod wsgid;
118}
119
120// Standard library imports
121use std::sync::Arc;
122use std::{error::Error, path::PathBuf};
123
124// External crate imports
125use clap::Parser;
126use ferron_server::start_server;
127use ferron_util::load_config::load_config;
128use mimalloc::MiMalloc;
129
130// Set the global allocator to use mimalloc for performance optimization
131#[global_allocator]
132static GLOBAL: MiMalloc = MiMalloc;
133
134// Struct for command-line arguments
135/// A fast, memory-safe web server written in Rust
136#[derive(Parser, Debug)]
137#[command(name = "Ferron")]
138#[command(version, about, long_about = None)]
139struct Args {
140  /// The path to the server configuration file
141  #[arg(short, long, default_value_t = String::from("./ferron.yaml"))]
142  config: String,
143}
144
145// Function to execute before starting the server
146#[allow(clippy::type_complexity)]
147fn before_starting_server(
148  args: &Args,
149  first_start: bool,
150) -> Result<bool, Box<dyn Error + Send + Sync>> {
151  // Load the configuration
152  let yaml_config = load_config(PathBuf::from(args.config.clone()))?;
153
154  let mut module_error = None;
155  let mut module_libs = Vec::new();
156
157  // Load external modules defined in the configuration file
158  if let Some(modules) = yaml_config["global"]["loadModules"].as_vec() {
159    for module_name_yaml in modules.iter() {
160      if let Some(module_name) = module_name_yaml.as_str() {
161        module_libs.push(String::from(module_name));
162      }
163    }
164  }
165
166  let mut external_modules = Vec::new();
167  #[allow(unused_mut)]
168  let mut modules_optional_builtin = Vec::new();
169  // Iterate over loaded module libraries and initialize them
170  for module_name in module_libs.iter() {
171    match module_name as &str {
172      #[cfg(feature = "rproxy")]
173      "rproxy" => {
174        external_modules.push(
175          match ferron_optional_modules::rproxy::server_module_init(&yaml_config) {
176            Ok(module) => module,
177            Err(err) => {
178              module_error = Some(anyhow::anyhow!(
179                "Cannot initialize optional built-in module \"{}\": {}",
180                module_name,
181                err
182              ));
183              break;
184            }
185          },
186        );
187
188        modules_optional_builtin.push(module_name.clone());
189      }
190      #[cfg(feature = "fproxy")]
191      "fproxy" => {
192        external_modules.push(
193          match ferron_optional_modules::fproxy::server_module_init(&yaml_config) {
194            Ok(module) => module,
195            Err(err) => {
196              module_error = Some(anyhow::anyhow!(
197                "Cannot initialize optional built-in module \"{}\": {}",
198                module_name,
199                err
200              ));
201              break;
202            }
203          },
204        );
205
206        modules_optional_builtin.push(module_name.clone());
207      }
208      #[cfg(feature = "cache")]
209      "cache" => {
210        external_modules.push(
211          match ferron_optional_modules::cache::server_module_init(&yaml_config) {
212            Ok(module) => module,
213            Err(err) => {
214              module_error = Some(anyhow::anyhow!(
215                "Cannot initialize optional built-in module \"{}\": {}",
216                module_name,
217                err
218              ));
219              break;
220            }
221          },
222        );
223
224        modules_optional_builtin.push(module_name.clone());
225      }
226      #[cfg(feature = "cgi")]
227      "cgi" => {
228        external_modules.push(
229          match ferron_optional_modules::cgi::server_module_init(&yaml_config) {
230            Ok(module) => module,
231            Err(err) => {
232              module_error = Some(anyhow::anyhow!(
233                "Cannot initialize optional built-in module \"{}\": {}",
234                module_name,
235                err
236              ));
237              break;
238            }
239          },
240        );
241
242        modules_optional_builtin.push(module_name.clone());
243      }
244      #[cfg(feature = "scgi")]
245      "scgi" => {
246        external_modules.push(
247          match ferron_optional_modules::scgi::server_module_init(&yaml_config) {
248            Ok(module) => module,
249            Err(err) => {
250              module_error = Some(anyhow::anyhow!(
251                "Cannot initialize optional built-in module \"{}\": {}",
252                module_name,
253                err
254              ));
255              break;
256            }
257          },
258        );
259
260        modules_optional_builtin.push(module_name.clone());
261      }
262      #[cfg(feature = "fcgi")]
263      "fcgi" => {
264        external_modules.push(
265          match ferron_optional_modules::fcgi::server_module_init(&yaml_config) {
266            Ok(module) => module,
267            Err(err) => {
268              module_error = Some(anyhow::anyhow!(
269                "Cannot initialize optional built-in module \"{}\": {}",
270                module_name,
271                err
272              ));
273              break;
274            }
275          },
276        );
277
278        modules_optional_builtin.push(module_name.clone());
279      }
280      #[cfg(feature = "fauth")]
281      "fauth" => {
282        external_modules.push(
283          match ferron_optional_modules::fauth::server_module_init(&yaml_config) {
284            Ok(module) => module,
285            Err(err) => {
286              module_error = Some(anyhow::anyhow!(
287                "Cannot initialize optional built-in module \"{}\": {}",
288                module_name,
289                err
290              ));
291              break;
292            }
293          },
294        );
295
296        modules_optional_builtin.push(module_name.clone());
297      }
298      #[cfg(feature = "example")]
299      "example" => {
300        external_modules.push(
301          match ferron_optional_modules::example::server_module_init(&yaml_config) {
302            Ok(module) => module,
303            Err(err) => {
304              module_error = Some(anyhow::anyhow!(
305                "Cannot initialize optional built-in module \"{}\": {}",
306                module_name,
307                err
308              ));
309              break;
310            }
311          },
312        );
313
314        modules_optional_builtin.push(module_name.clone());
315      }
316      #[cfg(feature = "wsgi")]
317      "wsgi" => {
318        external_modules.push(
319          match ferron_optional_modules::wsgi::server_module_init(&yaml_config) {
320            Ok(module) => module,
321            Err(err) => {
322              module_error = Some(anyhow::anyhow!(
323                "Cannot initialize optional built-in module \"{}\": {}",
324                module_name,
325                err
326              ));
327              break;
328            }
329          },
330        );
331
332        modules_optional_builtin.push(module_name.clone());
333      }
334      #[cfg(feature = "wsgid")]
335      "wsgid" => {
336        external_modules.push(
337          match ferron_optional_modules::wsgid::server_module_init(&yaml_config) {
338            Ok(module) => module,
339            Err(err) => {
340              module_error = Some(anyhow::anyhow!(
341                "Cannot initialize optional built-in module \"{}\": {}",
342                module_name,
343                err
344              ));
345              break;
346            }
347          },
348        );
349
350        modules_optional_builtin.push(module_name.clone());
351      }
352      #[cfg(feature = "asgi")]
353      "asgi" => {
354        external_modules.push(
355          match ferron_optional_modules::asgi::server_module_init(&yaml_config) {
356            Ok(module) => module,
357            Err(err) => {
358              module_error = Some(anyhow::anyhow!(
359                "Cannot initialize optional built-in module \"{}\": {}",
360                module_name,
361                err
362              ));
363              break;
364            }
365          },
366        );
367
368        modules_optional_builtin.push(module_name.clone());
369      }
370      _ => {
371        module_error = Some(anyhow::anyhow!(
372          "The optional built-in module \"{}\" doesn't exist",
373          module_name
374        ));
375        break;
376      }
377    }
378  }
379
380  // Add modules (both built-in and loaded)
381  let mut modules = Vec::new();
382  match ferron_modules::x_forwarded_for::server_module_init() {
383    Ok(module) => modules.push(module),
384    Err(err) => {
385      if module_error.is_none() {
386        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
387      }
388    }
389  };
390  match ferron_modules::redirects::server_module_init() {
391    Ok(module) => modules.push(module),
392    Err(err) => {
393      if module_error.is_none() {
394        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
395      }
396    }
397  };
398  match ferron_modules::blocklist::server_module_init(&yaml_config) {
399    Ok(module) => modules.push(module),
400    Err(err) => {
401      if module_error.is_none() {
402        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
403      }
404    }
405  };
406  match ferron_modules::url_rewrite::server_module_init(&yaml_config) {
407    Ok(module) => modules.push(module),
408    Err(err) => {
409      if module_error.is_none() {
410        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
411      }
412    }
413  };
414  match ferron_modules::non_standard_codes::server_module_init(&yaml_config) {
415    Ok(module) => modules.push(module),
416    Err(err) => {
417      if module_error.is_none() {
418        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
419      }
420    }
421  };
422  match ferron_modules::redirect_trailing_slashes::server_module_init() {
423    Ok(module) => modules.push(module),
424    Err(err) => {
425      if module_error.is_none() {
426        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
427      }
428    }
429  };
430  modules.append(&mut external_modules);
431  match ferron_modules::default_handler_checks::server_module_init() {
432    Ok(module) => modules.push(module),
433    Err(err) => {
434      if module_error.is_none() {
435        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
436      }
437    }
438  };
439  match ferron_modules::static_file_serving::server_module_init() {
440    Ok(module) => modules.push(module),
441    Err(err) => {
442      if module_error.is_none() {
443        module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
444      }
445    }
446  };
447
448  // Start the server with configuration and loaded modules
449  start_server(
450    Arc::new(yaml_config),
451    modules,
452    module_error,
453    modules_optional_builtin,
454    first_start,
455  )
456}
457
458// Entry point of the application
459fn main() {
460  let args = &Args::parse(); // Parse command-line arguments
461  let mut first_start = true;
462  loop {
463    match before_starting_server(args, first_start) {
464      Ok(false) => break,
465      Ok(true) => {
466        first_start = false;
467        println!("Reloading the server configuration...");
468      }
469      Err(err) => {
470        eprintln!("FATAL ERROR: {}", err);
471        std::process::exit(1);
472      }
473    }
474  }
475}