hyperi_rustlib/http_server/
config.rs1use serde::{Deserialize, Serialize};
12use std::time::Duration;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16#[allow(clippy::struct_excessive_bools)]
17pub struct HttpServerConfig {
18 pub bind_address: String,
20
21 #[serde(default = "default_request_timeout_ms")]
24 pub request_timeout_ms: u64,
25
26 #[serde(default = "default_keep_alive_timeout_ms")]
31 pub keep_alive_timeout_ms: u64,
32
33 #[serde(default = "default_max_connections")]
36 pub max_connections: usize,
37
38 #[serde(default = "default_true")]
40 pub enable_health_endpoints: bool,
41
42 #[serde(default)]
48 pub enable_metrics_endpoint: bool,
49
50 #[serde(default)]
52 pub enable_config_endpoint: bool,
53
54 #[serde(default = "default_true")]
59 pub enable_http2: bool,
60
61 #[serde(default)]
67 pub tls_cert_path: Option<String>,
68
69 #[serde(default)]
71 pub tls_key_path: Option<String>,
72
73 #[serde(default = "default_shutdown_timeout_ms")]
76 pub shutdown_timeout_ms: u64,
77}
78
79fn default_request_timeout_ms() -> u64 {
80 30_000
81}
82
83fn default_keep_alive_timeout_ms() -> u64 {
84 75_000
85}
86
87fn default_max_connections() -> usize {
88 10_000
89}
90
91fn default_shutdown_timeout_ms() -> u64 {
92 30_000
93}
94
95fn default_true() -> bool {
96 true
97}
98
99impl Default for HttpServerConfig {
100 fn default() -> Self {
101 Self {
102 bind_address: "0.0.0.0:8080".to_string(),
103 request_timeout_ms: default_request_timeout_ms(),
104 keep_alive_timeout_ms: default_keep_alive_timeout_ms(),
105 max_connections: default_max_connections(),
106 enable_health_endpoints: true,
107 enable_metrics_endpoint: false,
108 enable_config_endpoint: false,
109 enable_http2: true,
110 tls_cert_path: None,
111 tls_key_path: None,
112 shutdown_timeout_ms: default_shutdown_timeout_ms(),
113 }
114 }
115}
116
117impl HttpServerConfig {
118 #[must_use]
120 pub fn new(bind_address: impl Into<String>) -> Self {
121 Self {
122 bind_address: bind_address.into(),
123 ..Default::default()
124 }
125 }
126
127 #[must_use]
129 pub fn request_timeout(&self) -> Duration {
130 Duration::from_millis(self.request_timeout_ms)
131 }
132
133 #[must_use]
135 pub fn keep_alive_timeout(&self) -> Duration {
136 Duration::from_millis(self.keep_alive_timeout_ms)
137 }
138
139 #[must_use]
141 pub fn shutdown_timeout(&self) -> Duration {
142 Duration::from_millis(self.shutdown_timeout_ms)
143 }
144
145 #[must_use]
152 pub fn is_tls_enabled(&self) -> bool {
153 self.tls_cert_path.is_some() && self.tls_key_path.is_some()
154 }
155
156 pub fn validate(&self) -> Result<(), String> {
168 if self.tls_cert_path.is_some() || self.tls_key_path.is_some() {
169 return Err(
170 "http_server: in-process TLS is not supported (tls_cert_path / \
171 tls_key_path set) -- terminate TLS at the ingress / service mesh \
172 and leave these unset, or front the service with a TLS sidecar"
173 .to_string(),
174 );
175 }
176 Ok(())
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_default_config() {
186 let config = HttpServerConfig::default();
187 assert_eq!(config.bind_address, "0.0.0.0:8080");
188 assert_eq!(config.request_timeout_ms, 30_000);
189 assert_eq!(config.keep_alive_timeout_ms, 75_000);
190 assert_eq!(config.max_connections, 10_000);
191 assert!(config.enable_health_endpoints);
192 assert!(!config.enable_metrics_endpoint);
193 assert!(config.enable_http2);
194 assert!(!config.is_tls_enabled());
195 }
196
197 #[test]
198 fn test_new_with_address() {
199 let config = HttpServerConfig::new("127.0.0.1:3000");
200 assert_eq!(config.bind_address, "127.0.0.1:3000");
201 }
202
203 #[test]
204 fn test_tls_enabled() {
205 let mut config = HttpServerConfig::default();
206 assert!(!config.is_tls_enabled());
207
208 config.tls_cert_path = Some("/path/to/cert.pem".to_string());
209 assert!(!config.is_tls_enabled());
210
211 config.tls_key_path = Some("/path/to/key.pem".to_string());
212 assert!(config.is_tls_enabled());
213 }
214
215 #[test]
216 fn validate_rejects_unsupported_in_process_tls() {
217 assert!(HttpServerConfig::default().validate().is_ok());
219
220 let mut config = HttpServerConfig::default();
224 config.tls_cert_path = Some("/path/to/cert.pem".to_string());
225 assert!(!config.is_tls_enabled()); assert!(
227 config.validate().is_err(),
228 "a set TLS path must be rejected"
229 );
230
231 config.tls_key_path = Some("/path/to/key.pem".to_string());
232 assert!(config.is_tls_enabled());
233 assert!(
234 config.validate().is_err(),
235 "is_tls_enabled() true but in-process TLS unsupported -> reject"
236 );
237 }
238
239 #[test]
240 fn test_duration_conversions() {
241 let config = HttpServerConfig::default();
242 assert_eq!(config.request_timeout(), Duration::from_secs(30));
243 assert_eq!(config.keep_alive_timeout(), Duration::from_secs(75));
244 assert_eq!(config.shutdown_timeout(), Duration::from_secs(30));
245 }
246}