relay_knowledge/net/
mod.rs1use std::{
11 error::Error,
12 fmt,
13 sync::{Arc, RwLock},
14};
15
16use crate::env::{EnvError, EnvironmentConfig, NetworkEnvOverrides};
17
18pub mod http;
19pub mod qos;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct NetworkConfig {
24 pub http: http::HttpConfig,
25 pub qos: qos::QosPolicy,
26}
27
28impl NetworkConfig {
29 pub fn from_overrides(overrides: &NetworkEnvOverrides) -> Result<Self, NetworkConfigError> {
31 Ok(Self {
32 http: http::HttpConfig::from_overrides(overrides).map_err(NetworkConfigError::Http)?,
33 qos: qos::QosPolicy::from_overrides(overrides).map_err(NetworkConfigError::Qos)?,
34 })
35 }
36}
37
38#[derive(Debug, Clone)]
40pub struct NetworkRuntime {
41 inner: Arc<RwLock<NetworkConfig>>,
42 qos: qos::QosRuntime,
43}
44
45impl NetworkRuntime {
46 pub fn from_config(config: NetworkConfig) -> Self {
48 Self {
49 inner: Arc::new(RwLock::new(config)),
50 qos: qos::QosRuntime::default(),
51 }
52 }
53
54 pub fn from_overrides(overrides: &NetworkEnvOverrides) -> Result<Self, NetworkConfigError> {
56 NetworkConfig::from_overrides(overrides).map(Self::from_config)
57 }
58
59 pub fn current(&self) -> NetworkConfig {
61 self.inner
62 .read()
63 .unwrap_or_else(|poisoned| poisoned.into_inner())
64 .clone()
65 }
66
67 pub fn qos_runtime(&self) -> qos::QosRuntime {
69 self.qos.clone()
70 }
71
72 pub fn refresh_from_overrides(
74 &self,
75 overrides: &NetworkEnvOverrides,
76 ) -> Result<NetworkConfig, NetworkConfigError> {
77 let config = NetworkConfig::from_overrides(overrides)?;
78
79 *self
80 .inner
81 .write()
82 .unwrap_or_else(|poisoned| poisoned.into_inner()) = config.clone();
83
84 Ok(config)
85 }
86
87 pub fn refresh_from_environment(
89 &self,
90 environment: &EnvironmentConfig,
91 ) -> Result<NetworkConfig, NetworkConfigError> {
92 self.refresh_from_overrides(&environment.network)
93 }
94
95 pub fn refresh_from_process_environment(&self) -> Result<NetworkConfig, NetworkRuntimeError> {
97 let environment =
98 EnvironmentConfig::from_process().map_err(NetworkRuntimeError::Environment)?;
99
100 self.refresh_from_environment(&environment)
101 .map_err(NetworkRuntimeError::Config)
102 }
103}
104
105#[derive(Debug, Clone, PartialEq, Eq)]
107pub enum NetworkConfigError {
108 Http(http::HttpConfigError),
109 Qos(qos::QosPolicyError),
110}
111
112impl fmt::Display for NetworkConfigError {
113 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self {
115 Self::Http(error) => write!(formatter, "invalid HTTP configuration: {error}"),
116 Self::Qos(error) => write!(formatter, "invalid QoS policy: {error}"),
117 }
118 }
119}
120
121impl Error for NetworkConfigError {}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
125pub enum NetworkRuntimeError {
126 Environment(EnvError),
127 Config(NetworkConfigError),
128}
129
130impl fmt::Display for NetworkRuntimeError {
131 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132 match self {
133 Self::Environment(error) => write!(formatter, "{error}"),
134 Self::Config(error) => write!(formatter, "{error}"),
135 }
136 }
137}
138
139impl Error for NetworkRuntimeError {}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144 use crate::env::PlatformKind;
145
146 #[test]
147 fn resolves_default_network_configuration() {
148 let config = NetworkConfig::from_overrides(&NetworkEnvOverrides::default())
149 .expect("defaults should resolve");
150
151 assert_eq!(config.http.bind_address.to_string(), "127.0.0.1:8791");
152 assert!(!config.http.proxy.is_proxy_configured());
153 assert!(config.http.proxy.ssl_verify);
154 assert_eq!(config.qos.max_connections, 1024);
155 assert_eq!(config.qos.max_in_flight_requests, 256);
156 assert_eq!(config.qos.max_queue_depth, 512);
157 }
158
159 #[test]
160 fn refreshes_runtime_network_config_from_environment_snapshot() {
161 let runtime = NetworkRuntime::from_overrides(&NetworkEnvOverrides::default())
162 .expect("runtime should build");
163 let environment = EnvironmentConfig::from_pairs(
164 PlatformKind::Unix,
165 [
166 ("HTTP_PROXY", "http://relay-proxy:8080"),
167 ("NO_PROXY", "localhost"),
168 ("SSL_VERIFY", "false"),
169 ("RELAY_KNOWLEDGE_QOS_MAX_CONNECTIONS", "8"),
170 ],
171 )
172 .expect("environment should parse");
173
174 runtime
175 .refresh_from_environment(&environment)
176 .expect("network refresh should succeed");
177 let config = runtime.current();
178
179 assert_eq!(
180 config.http.proxy.proxy,
181 Some("http://relay-proxy:8080".to_owned())
182 );
183 assert_eq!(config.http.proxy.no_proxy_rules, ["localhost"]);
184 assert!(!config.http.proxy.ssl_verify);
185 assert_eq!(config.qos.max_connections, 8);
186 }
187}