#[cfg(test)]
mod tests;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, RwLock};
use crate::entry_point::Config;
static GENERATION: AtomicU64 = AtomicU64::new(1);
static CURRENT: RwLock<Option<(u64, Arc<ServerConfig>)>> = RwLock::new(None);
pub fn invalidate() {
GENERATION.fetch_add(1, Ordering::SeqCst);
}
const CSP_DEFAULT: &str = "default-src 'self'";
pub(crate) const STATIC_CACHE_CONTROL_DEFAULT: &str = "no-cache";
#[derive(Clone, Debug, PartialEq)]
pub struct ServerConfig {
pub cors_allow_all: bool,
pub cors_allow_origins: String,
pub cors_allow_credentials: String,
pub cors_allow_methods: String,
pub cors_allow_headers: String,
pub cors_expose_headers: String,
pub cors_max_age: String,
pub csp: String,
pub security_headers: bool,
pub nanos_headers: bool,
pub client_hints: bool,
pub static_cache_control: String,
pub log_format: String,
pub request_allocation_size: i64,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
cors_allow_all: Config::RWS_CONFIG_CORS_ALLOW_ALL_DEFAULT_VALUE
.eq_ignore_ascii_case("true"),
cors_allow_origins: Config::RWS_CONFIG_CORS_ALLOW_ORIGINS_DEFAULT_VALUE.to_string(),
cors_allow_credentials: Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS_DEFAULT_VALUE
.to_string(),
cors_allow_methods: Config::RWS_CONFIG_CORS_ALLOW_METHODS_DEFAULT_VALUE.to_string(),
cors_allow_headers: Config::RWS_CONFIG_CORS_ALLOW_HEADERS_DEFAULT_VALUE.to_string(),
cors_expose_headers: Config::RWS_CONFIG_CORS_EXPOSE_HEADERS_DEFAULT_VALUE.to_string(),
cors_max_age: Config::RWS_CONFIG_CORS_MAX_AGE_DEFAULT_VALUE.to_string(),
csp: String::new(),
security_headers: false,
nanos_headers: false,
client_hints: false,
static_cache_control: STATIC_CACHE_CONTROL_DEFAULT.to_string(),
log_format: Config::RWS_CONFIG_LOG_FORMAT_DEFAULT_VALUE.to_string(),
request_allocation_size: *Config::RWS_DEFAULT_REQUEST_ALLOCATION_SIZE_IN_BYTES,
}
}
}
impl ServerConfig {
pub fn from_env() -> Self {
let read = |key: &str| std::env::var(key).unwrap_or_default();
Self {
cors_allow_all: read(Config::RWS_CONFIG_CORS_ALLOW_ALL)
.eq_ignore_ascii_case("true"),
cors_allow_origins: read(Config::RWS_CONFIG_CORS_ALLOW_ORIGINS),
cors_allow_credentials: read(Config::RWS_CONFIG_CORS_ALLOW_CREDENTIALS),
cors_allow_methods: read(Config::RWS_CONFIG_CORS_ALLOW_METHODS),
cors_allow_headers: read(Config::RWS_CONFIG_CORS_ALLOW_HEADERS),
cors_expose_headers: read(Config::RWS_CONFIG_CORS_EXPOSE_HEADERS),
cors_max_age: read(Config::RWS_CONFIG_CORS_MAX_AGE),
csp: std::env::var("RWS_CONFIG_CSP").unwrap_or_else(|_| {
if read("RWS_CONFIG_SECURITY_HEADERS").eq_ignore_ascii_case("true") {
CSP_DEFAULT.to_string()
} else {
String::new()
}
}),
security_headers: read("RWS_CONFIG_SECURITY_HEADERS").eq_ignore_ascii_case("true"),
nanos_headers: read("RWS_CONFIG_NANOS_HEADERS").eq_ignore_ascii_case("true"),
client_hints: read("RWS_CONFIG_CLIENT_HINTS").eq_ignore_ascii_case("true"),
static_cache_control: std::env::var("RWS_CONFIG_STATIC_CACHE_CONTROL")
.unwrap_or_else(|_| STATIC_CACHE_CONTROL_DEFAULT.to_string()),
log_format: read(Config::RWS_CONFIG_LOG_FORMAT),
request_allocation_size: std::env::var(
Config::RWS_CONFIG_REQUEST_ALLOCATION_SIZE_IN_BYTES,
)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(*Config::RWS_DEFAULT_REQUEST_ALLOCATION_SIZE_IN_BYTES),
}
}
pub fn current() -> Arc<ServerConfig> {
let generation = GENERATION.load(Ordering::SeqCst);
if let Some((built_at, config)) = CURRENT.read().unwrap_or_else(|e| e.into_inner()).as_ref() {
if *built_at == generation {
return Arc::clone(config);
}
}
let fresh = Arc::new(ServerConfig::from_env());
*CURRENT.write().unwrap_or_else(|e| e.into_inner()) = Some((generation, Arc::clone(&fresh)));
fresh
}
}