rust-rpxy/src/config/parse.rs

116 lines
3.5 KiB
Rust

use super::toml::ConfigToml;
use crate::{
backend::Backends,
cert_file_reader::CryptoFileSource,
error::{anyhow, ensure},
globals::*,
log::*,
utils::BytesName,
};
use clap::Arg;
use tokio::runtime::Handle;
pub fn build_globals(runtime_handle: Handle) -> std::result::Result<Globals<CryptoFileSource>, anyhow::Error> {
let _ = include_str!("../../Cargo.toml");
let options = clap::command!().arg(
Arg::new("config_file")
.long("config")
.short('c')
.value_name("FILE")
.help("Configuration file path like \"./config.toml\""),
);
let matches = options.get_matches();
///////////////////////////////////
let config = if let Some(config_file_path) = matches.get_one::<String>("config_file") {
ConfigToml::new(config_file_path)?
} else {
// Default config Toml
ConfigToml::default()
};
///////////////////////////////////
// build proxy config
let proxy_config: ProxyConfig = (&config).try_into()?;
// For loggings
if proxy_config.listen_sockets.iter().any(|addr| addr.is_ipv6()) {
info!("Listen both IPv4 and IPv6")
} else {
info!("Listen IPv4")
}
if proxy_config.http_port.is_some() {
info!("Listen port: {}", proxy_config.http_port.unwrap());
}
if proxy_config.https_port.is_some() {
info!("Listen port: {} (for TLS)", proxy_config.https_port.unwrap());
}
if proxy_config.http3 {
info!("Experimental HTTP/3.0 is enabled. Note it is still very unstable.");
}
if !proxy_config.sni_consistency {
info!("Ignore consistency between TLS SNI and Host header (or Request line). Note it violates RFC.");
}
///////////////////////////////////
// backend_apps
let apps = config.apps.ok_or(anyhow!("Missing application spec"))?;
// assertions for all backend apps
ensure!(!apps.0.is_empty(), "Wrong application spec.");
// if only https_port is specified, tls must be configured for all apps
if proxy_config.http_port.is_none() {
ensure!(
apps.0.iter().all(|(_, app)| app.tls.is_some()),
"Some apps serves only plaintext HTTP"
);
}
// https redirection can be configured if both ports are active
if !(proxy_config.https_port.is_some() && proxy_config.http_port.is_some()) {
ensure!(
apps.0.iter().all(|(_, app)| {
if let Some(tls) = app.tls.as_ref() {
tls.https_redirection.is_none()
} else {
true
}
}),
"https_redirection can be specified only when both http_port and https_port are specified"
);
}
// build backends
let mut backends = Backends::new();
for (app_name, app) in apps.0.iter() {
let server_name_string = app.server_name.as_ref().ok_or(anyhow!("No server name"))?;
let backend = app.try_into()?;
backends.apps.insert(server_name_string.to_server_name_vec(), backend);
info!("Registering application: {} ({})", app_name, server_name_string);
}
// default backend application for plaintext http requests
if let Some(d) = config.default_app {
let d_sn: Vec<&str> = backends
.apps
.iter()
.filter(|(_k, v)| v.app_name == d)
.map(|(_, v)| v.server_name.as_ref())
.collect();
if !d_sn.is_empty() {
info!(
"Serving plaintext http for requests to unconfigured server_name by app {} (server_name: {}).",
d, d_sn[0]
);
backends.default_server_name_bytes = Some(d_sn[0].to_server_name_vec());
}
}
///////////////////////////////////
let globals = Globals {
proxy_config,
backends,
request_count: Default::default(),
runtime_handle,
};
Ok(globals)
}