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