Skip to main content

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