use std::{
error::Error,
fmt,
sync::{Arc, RwLock},
};
use crate::env::{EnvError, EnvironmentConfig, NetworkEnvOverrides};
pub mod http;
pub mod qos;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NetworkConfig {
pub http: http::HttpConfig,
pub qos: qos::QosPolicy,
}
impl NetworkConfig {
pub fn from_overrides(overrides: &NetworkEnvOverrides) -> Result<Self, NetworkConfigError> {
Ok(Self {
http: http::HttpConfig::from_overrides(overrides).map_err(NetworkConfigError::Http)?,
qos: qos::QosPolicy::from_overrides(overrides).map_err(NetworkConfigError::Qos)?,
})
}
}
#[derive(Debug, Clone)]
pub struct NetworkRuntime {
inner: Arc<RwLock<NetworkConfig>>,
}
impl NetworkRuntime {
pub fn from_config(config: NetworkConfig) -> Self {
Self {
inner: Arc::new(RwLock::new(config)),
}
}
pub fn from_overrides(overrides: &NetworkEnvOverrides) -> Result<Self, NetworkConfigError> {
NetworkConfig::from_overrides(overrides).map(Self::from_config)
}
pub fn current(&self) -> NetworkConfig {
self.inner
.read()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.clone()
}
pub fn refresh_from_overrides(
&self,
overrides: &NetworkEnvOverrides,
) -> Result<NetworkConfig, NetworkConfigError> {
let config = NetworkConfig::from_overrides(overrides)?;
*self
.inner
.write()
.unwrap_or_else(|poisoned| poisoned.into_inner()) = config.clone();
Ok(config)
}
pub fn refresh_from_environment(
&self,
environment: &EnvironmentConfig,
) -> Result<NetworkConfig, NetworkConfigError> {
self.refresh_from_overrides(&environment.network)
}
pub fn refresh_from_process_environment(&self) -> Result<NetworkConfig, NetworkRuntimeError> {
let environment =
EnvironmentConfig::from_process().map_err(NetworkRuntimeError::Environment)?;
self.refresh_from_environment(&environment)
.map_err(NetworkRuntimeError::Config)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NetworkConfigError {
Http(http::HttpConfigError),
Qos(qos::QosPolicyError),
}
impl fmt::Display for NetworkConfigError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Http(error) => write!(formatter, "invalid HTTP configuration: {error}"),
Self::Qos(error) => write!(formatter, "invalid QoS policy: {error}"),
}
}
}
impl Error for NetworkConfigError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NetworkRuntimeError {
Environment(EnvError),
Config(NetworkConfigError),
}
impl fmt::Display for NetworkRuntimeError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Environment(error) => write!(formatter, "{error}"),
Self::Config(error) => write!(formatter, "{error}"),
}
}
}
impl Error for NetworkRuntimeError {}
#[cfg(test)]
mod tests {
use super::*;
use crate::env::PlatformKind;
#[test]
fn resolves_default_network_configuration() {
let config = NetworkConfig::from_overrides(&NetworkEnvOverrides::default())
.expect("defaults should resolve");
assert_eq!(config.http.bind_address.to_string(), "127.0.0.1:8791");
assert!(!config.http.proxy.is_proxy_configured());
assert!(config.http.proxy.ssl_verify);
assert_eq!(config.qos.max_connections, 1024);
assert_eq!(config.qos.max_in_flight_requests, 256);
assert_eq!(config.qos.max_queue_depth, 512);
}
#[test]
fn refreshes_runtime_network_config_from_environment_snapshot() {
let runtime = NetworkRuntime::from_overrides(&NetworkEnvOverrides::default())
.expect("runtime should build");
let environment = EnvironmentConfig::from_pairs(
PlatformKind::Unix,
[
("HTTP_PROXY", "http://relay-proxy:8080"),
("NO_PROXY", "localhost"),
("SSL_VERIFY", "false"),
("RELAY_KNOWLEDGE_QOS_MAX_CONNECTIONS", "8"),
],
)
.expect("environment should parse");
runtime
.refresh_from_environment(&environment)
.expect("network refresh should succeed");
let config = runtime.current();
assert_eq!(
config.http.proxy.proxy,
Some("http://relay-proxy:8080".to_owned())
);
assert_eq!(config.http.proxy.no_proxy_rules, ["localhost"]);
assert!(!config.http.proxy.ssl_verify);
assert_eq!(config.qos.max_connections, 8);
}
}