#[path = "server.rs"]
mod ferron_server;
#[path = "request_handler.rs"]
mod ferron_request_handler;
#[path = "res"]
mod ferron_res {
pub mod server_software;
}
#[path = "common/mod.rs"]
mod ferron_common;
#[path = "util"]
mod ferron_util {
pub mod anti_xss;
#[cfg(any(feature = "cgi", feature = "scgi", feature = "fcgi"))]
pub mod cgi_response;
pub mod combine_config;
#[cfg(any(feature = "cgi", feature = "scgi", feature = "fcgi"))]
pub mod copy_move;
pub mod error_pages;
#[cfg(feature = "fcgi")]
pub mod fcgi_decoder;
#[cfg(feature = "fcgi")]
pub mod fcgi_encoder;
#[cfg(feature = "fcgi")]
pub mod fcgi_name_value_pair;
#[cfg(feature = "fcgi")]
pub mod fcgi_record;
pub mod generate_directory_listing;
pub mod ip_blocklist;
pub mod ip_match;
pub mod load_config;
pub mod load_tls;
pub mod match_hostname;
pub mod match_location;
#[cfg(any(feature = "rproxy", feature = "fauth"))]
pub mod no_server_verifier;
pub mod non_standard_code_structs;
#[cfg(feature = "fcgi")]
pub mod read_to_end_move;
pub mod sizify;
pub mod sni;
#[cfg(feature = "fcgi")]
pub mod split_stream_by_map;
pub mod ttl_cache;
pub mod url_rewrite_structs;
pub mod url_sanitizer;
pub mod validate_config;
}
#[path = "modules"]
mod ferron_modules {
pub mod blocklist;
pub mod default_handler_checks;
pub mod non_standard_codes;
pub mod redirect_trailing_slashes;
pub mod redirects;
pub mod static_file_serving;
pub mod url_rewrite;
pub mod x_forwarded_for;
}
#[path = "optional_modules"]
mod ferron_optional_modules {
#[cfg(feature = "cache")]
pub mod cache;
#[cfg(feature = "cgi")]
pub mod cgi;
#[cfg(feature = "example")]
pub mod example;
#[cfg(feature = "fauth")]
pub mod fauth;
#[cfg(feature = "fcgi")]
pub mod fcgi;
#[cfg(feature = "fproxy")]
pub mod fproxy;
#[cfg(feature = "rproxy")]
pub mod rproxy;
#[cfg(feature = "scgi")]
pub mod scgi;
}
use std::sync::Arc;
use std::{error::Error, path::PathBuf};
use clap::Parser;
use ferron_server::start_server;
use ferron_util::load_config::load_config;
use mimalloc::MiMalloc;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
#[derive(Parser, Debug)]
#[command(name = "Ferron")]
#[command(version, about, long_about = None)]
struct Args {
#[arg(short, long, default_value_t = String::from("./ferron.yaml"))]
config: String,
}
#[allow(clippy::type_complexity)]
fn before_starting_server(
args: &Args,
first_start: bool,
) -> Result<bool, Box<dyn Error + Send + Sync>> {
let yaml_config = load_config(PathBuf::from(args.config.clone()))?;
let mut module_error = None;
let mut module_libs = Vec::new();
if let Some(modules) = yaml_config["global"]["loadModules"].as_vec() {
for module_name_yaml in modules.iter() {
if let Some(module_name) = module_name_yaml.as_str() {
module_libs.push(String::from(module_name));
}
}
}
let mut external_modules = Vec::new();
#[allow(unused_mut)]
let mut modules_optional_builtin = Vec::new();
for module_name in module_libs.iter() {
match module_name as &str {
#[cfg(feature = "rproxy")]
"rproxy" => {
external_modules.push(
match ferron_optional_modules::rproxy::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
#[cfg(feature = "fproxy")]
"fproxy" => {
external_modules.push(
match ferron_optional_modules::fproxy::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
#[cfg(feature = "cache")]
"cache" => {
external_modules.push(
match ferron_optional_modules::cache::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
#[cfg(feature = "cgi")]
"cgi" => {
external_modules.push(
match ferron_optional_modules::cgi::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
#[cfg(feature = "scgi")]
"scgi" => {
external_modules.push(
match ferron_optional_modules::scgi::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
#[cfg(feature = "fcgi")]
"fcgi" => {
external_modules.push(
match ferron_optional_modules::fcgi::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
#[cfg(feature = "fauth")]
"fauth" => {
external_modules.push(
match ferron_optional_modules::fauth::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
#[cfg(feature = "example")]
"example" => {
external_modules.push(
match ferron_optional_modules::example::server_module_init(&yaml_config) {
Ok(module) => module,
Err(err) => {
module_error = Some(anyhow::anyhow!(
"Cannot initialize optional built-in module \"{}\": {}",
module_name,
err
));
break;
}
},
);
modules_optional_builtin.push(module_name.clone());
}
_ => {
module_error = Some(anyhow::anyhow!(
"The optional built-in module \"{}\" doesn't exist",
module_name
));
break;
}
}
}
let mut modules = Vec::new();
match ferron_modules::x_forwarded_for::server_module_init() {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
match ferron_modules::redirects::server_module_init() {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
match ferron_modules::blocklist::server_module_init(&yaml_config) {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
match ferron_modules::url_rewrite::server_module_init(&yaml_config) {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
match ferron_modules::non_standard_codes::server_module_init(&yaml_config) {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
match ferron_modules::redirect_trailing_slashes::server_module_init() {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
modules.append(&mut external_modules);
match ferron_modules::default_handler_checks::server_module_init() {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
match ferron_modules::static_file_serving::server_module_init() {
Ok(module) => modules.push(module),
Err(err) => {
if module_error.is_none() {
module_error = Some(anyhow::anyhow!("Cannot load a built-in module: {}", err));
}
}
};
start_server(
Arc::new(yaml_config),
modules,
module_error,
modules_optional_builtin,
first_start,
)
}
fn main() {
let args = &Args::parse(); let mut first_start = true;
loop {
match before_starting_server(args, first_start) {
Ok(false) => break,
Ok(true) => {
first_start = false;
println!("Reloading the server configuration...");
}
Err(err) => {
eprintln!("FATAL ERROR: {}", err);
std::process::exit(1);
}
}
}
}