1use duration_string::DurationString;
20use schemars::JsonSchema;
21use serde::{Deserialize, Serialize};
22use std::sync::Arc;
23use std::time::Duration;
24use tokio_util::sync::CancellationToken;
25
26use crate::server_handler::ServerHandler;
27
28use crate::auth::basic::Config as BasicAuthenticationConfig;
29use crate::auth::jwt::Config as JwtAuthenticationConfig;
30use crate::auth::oidc::Config as OidcAuthConfig;
31#[cfg(not(target_family = "windows"))]
32use crate::auth::spire::SpireConfig as SpireAuthConfig;
33use crate::component::configuration::Configuration;
34use crate::errors::ConfigError;
35use crate::tls::server::TlsServerConfig as TLSSetting;
36use crate::transport::{TransportProtocol, validate_endpoint_scheme};
37use slim_auth::metadata::MetadataMap;
38
39#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, JsonSchema)]
40pub struct KeepaliveServerParameters {
41 #[serde(default = "default_max_connection_idle")]
43 #[schemars(with = "String")]
44 pub max_connection_idle: DurationString,
45
46 #[serde(default = "default_max_connection_age")]
48 #[schemars(with = "String")]
49 pub max_connection_age: DurationString,
50
51 #[serde(default = "default_max_connection_age_grace")]
53 #[schemars(with = "String")]
54 pub max_connection_age_grace: DurationString,
55
56 #[serde(default = "default_time")]
58 #[schemars(with = "String")]
59 pub time: DurationString,
60
61 #[serde(default = "default_timeout")]
63 #[schemars(with = "String")]
64 pub timeout: DurationString,
65}
66
67#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, JsonSchema)]
69#[serde(rename_all = "snake_case", tag = "type")]
70pub enum AuthenticationConfig {
71 Basic(BasicAuthenticationConfig),
73 Jwt(JwtAuthenticationConfig),
75 Oidc(OidcAuthConfig),
77 #[cfg(not(target_family = "windows"))]
79 Spire(SpireAuthConfig),
80 #[default]
82 None,
83}
84
85#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, JsonSchema)]
86pub struct ServerConfig {
87 pub endpoint: String,
93
94 #[serde(default, rename = "tls")]
96 pub tls_setting: TLSSetting,
97
98 #[serde(default = "default_http2_only")]
100 pub http2_only: bool,
101
102 pub max_frame_size: Option<u32>,
104
105 pub max_concurrent_streams: Option<u32>,
107
108 pub max_header_list_size: Option<u32>,
110
111 pub read_buffer_size: Option<usize>,
114
115 pub write_buffer_size: Option<usize>,
118
119 #[serde(default)]
121 pub keepalive: KeepaliveServerParameters,
122
123 #[serde(default)]
125 pub auth: AuthenticationConfig,
126
127 pub metadata: Option<MetadataMap>,
129
130 #[serde(default = "default_require_header_mac")]
133 pub require_header_mac: bool,
134
135 #[serde(
138 default = "default_negotiation_timeout_secs",
139 alias = "link_hmac_timeout_secs"
140 )]
141 pub negotiation_timeout_secs: u64,
142
143 #[serde(default = "default_link_hmac_poll_interval_ms")]
146 pub link_hmac_poll_interval_ms: u64,
147}
148
149impl Default for KeepaliveServerParameters {
151 fn default() -> Self {
152 Self {
153 max_connection_idle: default_max_connection_idle(),
154 max_connection_age: default_max_connection_age(),
155 max_connection_age_grace: default_max_connection_age_grace(),
156 time: default_time(),
157 timeout: default_timeout(),
158 }
159 }
160}
161
162fn default_max_connection_idle() -> DurationString {
163 Duration::from_secs(3600).into()
164}
165
166fn default_max_connection_age() -> DurationString {
167 Duration::from_secs(2 * 3600).into()
168}
169
170fn default_max_connection_age_grace() -> DurationString {
171 Duration::from_secs(5 * 60).into()
172}
173
174fn default_time() -> DurationString {
175 Duration::from_secs(2 * 60).into()
176}
177
178fn default_timeout() -> DurationString {
179 Duration::from_secs(20).into()
180}
181
182impl Default for ServerConfig {
184 fn default() -> Self {
185 Self {
186 endpoint: String::new(),
187 tls_setting: TLSSetting::default(),
188 http2_only: default_http2_only(),
189 max_frame_size: Some(4),
190 max_concurrent_streams: Some(100),
191 max_header_list_size: None,
192 read_buffer_size: Some(1024 * 1024),
193 write_buffer_size: Some(1024 * 1024),
194 keepalive: KeepaliveServerParameters::default(),
195 auth: AuthenticationConfig::default(),
196 metadata: None,
197 require_header_mac: true,
198 negotiation_timeout_secs: default_negotiation_timeout_secs(),
199 link_hmac_poll_interval_ms: default_link_hmac_poll_interval_ms(),
200 }
201 }
202}
203
204fn default_http2_only() -> bool {
205 true
206}
207
208fn default_require_header_mac() -> bool {
209 true
210}
211
212fn default_negotiation_timeout_secs() -> u64 {
213 5
214}
215
216fn default_link_hmac_poll_interval_ms() -> u64 {
217 5
218}
219
220impl std::fmt::Display for ServerConfig {
222 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223 write!(
224 f,
225 "ServerConfig {{ endpoint: {}, transport: {:?}, tls_setting: {}, http2_only: {}, max_frame_size: {:?}, max_concurrent_streams: {:?}, max_header_list_size: {:?}, read_buffer_size: {:?}, write_buffer_size: {:?}, keepalive: {:?}, auth: {:?}, metadata: {:?}, require_header_mac: {}, negotiation_timeout_secs: {}, link_hmac_poll_interval_ms: {} }}",
226 self.endpoint,
227 self.resolved_transport(),
228 self.tls_setting,
229 self.http2_only,
230 self.max_frame_size,
231 self.max_concurrent_streams,
232 self.max_header_list_size,
233 self.read_buffer_size,
234 self.write_buffer_size,
235 self.keepalive,
236 self.auth,
237 self.metadata,
238 self.require_header_mac,
239 self.negotiation_timeout_secs,
240 self.link_hmac_poll_interval_ms,
241 )
242 }
243}
244
245impl Configuration for ServerConfig {
246 type Error = ConfigError;
247
248 fn validate(&self) -> Result<(), Self::Error> {
249 self.tls_setting.validate()?;
250 validate_endpoint_scheme(&self.endpoint)?;
251 Ok(())
252 }
253}
254
255impl ServerConfig {
256 pub fn with_endpoint(endpoint: &str) -> Self {
257 Self {
258 endpoint: endpoint.to_string(),
259 ..Default::default()
260 }
261 }
262
263 pub fn with_tls_settings(self, tls_setting: TLSSetting) -> Self {
264 Self {
265 tls_setting,
266 ..self
267 }
268 }
269
270 pub fn with_http2_only(self, http2_only: bool) -> Self {
271 Self { http2_only, ..self }
272 }
273
274 pub fn with_max_frame_size(self, max_frame_size: Option<u32>) -> Self {
275 Self {
276 max_frame_size,
277 ..self
278 }
279 }
280
281 pub fn with_max_concurrent_streams(self, max_concurrent_streams: Option<u32>) -> Self {
282 Self {
283 max_concurrent_streams,
284 ..self
285 }
286 }
287
288 pub fn with_max_header_list_size(self, max_header_list_size: Option<u32>) -> Self {
289 Self {
290 max_header_list_size,
291 ..self
292 }
293 }
294
295 pub fn with_read_buffer_size(self, read_buffer_size: Option<usize>) -> Self {
296 Self {
297 read_buffer_size,
298 ..self
299 }
300 }
301
302 pub fn with_write_buffer_size(self, write_buffer_size: Option<usize>) -> Self {
303 Self {
304 write_buffer_size,
305 ..self
306 }
307 }
308
309 pub fn with_keepalive(self, keepalive: KeepaliveServerParameters) -> Self {
310 Self { keepalive, ..self }
311 }
312
313 pub fn with_auth(self, auth: AuthenticationConfig) -> Self {
314 Self { auth, ..self }
315 }
316
317 pub async fn run_server<H: ServerHandler>(
324 &self,
325 watch: drain::Watch,
326 handler: Arc<H>,
327 ) -> Result<CancellationToken, ConfigError> {
328 match self.resolved_transport() {
329 TransportProtocol::Grpc => {
330 let routes = handler
331 .grpc_routes()
332 .ok_or(ConfigError::HandlerMissingGrpcSupport)?;
333 self.run_grpc_server_with_routes(routes, watch).await
334 }
335 TransportProtocol::Websocket => {
336 let on_accepted = handler
337 .on_websocket_accepted()
338 .ok_or(ConfigError::HandlerMissingWebSocketSupport)?;
339 self.run_websocket_server(watch, on_accepted).await
340 }
341 }
342 }
343
344 pub fn resolved_transport(&self) -> TransportProtocol {
347 TransportProtocol::from_endpoint(&self.endpoint)
348 }
349}
350
351#[cfg(test)]
352mod metadata_tests {
353 use super::*;
354
355 #[test]
356 fn server_config_with_metadata_roundtrip_yaml() {
357 let mut md = MetadataMap::default();
358 md.insert("role", "ingress");
359 md.insert("replicas", 3u64);
360 let mut nested = MetadataMap::default();
361 nested.insert("inner", "v");
362 md.insert("nested", nested);
363
364 let cfg = ServerConfig {
365 endpoint: "127.0.0.1:50051".to_string(),
366 metadata: Some(md.clone()),
367 ..Default::default()
368 };
369
370 let s = serde_yaml::to_string(&cfg).expect("serialize");
371 let deser: ServerConfig = serde_yaml::from_str(&s).expect("deserialize");
372 assert_eq!(deser.metadata, Some(md));
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379
380 #[test]
381 fn test_validate_rejects_unknown_scheme() {
382 let cfg = ServerConfig::with_endpoint("ftp://0.0.0.0:46357");
383 assert!(matches!(
384 cfg.validate(),
385 Err(ConfigError::InvalidEndpointScheme)
386 ));
387 }
388
389 #[test]
390 fn test_validate_accepts_supported_schemes() {
391 for endpoint in [
392 "0.0.0.0:46357",
393 "http://0.0.0.0:46357",
394 "https://0.0.0.0:46357",
395 "ws://0.0.0.0:46357",
396 "wss://0.0.0.0:46357",
397 "unix:///tmp/slim.sock",
398 ] {
399 let cfg = ServerConfig::with_endpoint(endpoint);
400 cfg.validate()
401 .unwrap_or_else(|e| panic!("endpoint {endpoint} rejected: {e}"));
402 }
403 }
404
405 #[test]
406 fn test_default_keepalive_server_parameters() {
407 let keepalive = KeepaliveServerParameters::default();
408 assert_eq!(keepalive.max_connection_idle, default_max_connection_idle());
409 assert_eq!(keepalive.max_connection_age, default_max_connection_age());
410 assert_eq!(
411 keepalive.max_connection_age_grace,
412 default_max_connection_age_grace()
413 );
414 assert_eq!(keepalive.time, default_time());
415 assert_eq!(keepalive.timeout, default_timeout());
416 }
417
418 #[test]
419 fn test_default_server_config() {
420 let server_config = ServerConfig::default();
421 assert_eq!(server_config.endpoint, String::new());
422 assert_eq!(server_config.resolved_transport(), TransportProtocol::Grpc);
423 assert_eq!(server_config.tls_setting, TLSSetting::default());
424 assert_eq!(server_config.http2_only, default_http2_only());
425 assert_eq!(server_config.max_frame_size, Some(4));
426 assert_eq!(server_config.max_concurrent_streams, Some(100));
427 assert_eq!(server_config.max_header_list_size, None);
428 assert_eq!(server_config.read_buffer_size, Some(1024 * 1024));
429 assert_eq!(server_config.write_buffer_size, Some(1024 * 1024));
430 assert_eq!(
431 server_config.keepalive,
432 KeepaliveServerParameters::default()
433 );
434 assert_eq!(server_config.auth, AuthenticationConfig::None);
435 }
436
437 #[test]
438 fn test_keepalive_server_parameters_valid_durations_deserialize() {
439 let json = r#"{
440 "endpoint": "0.0.0.0:12345",
441 "keepalive": {
442 "max_connection_idle": "30m",
443 "max_connection_age": "1h30m",
444 "max_connection_age_grace": "15s",
445 "time": "5s",
446 "timeout": "2s"
447 }
448 }"#;
449
450 let cfg: ServerConfig = serde_json::from_str(json).expect("deserialization should succeed");
451 assert_eq!(
452 cfg.keepalive.max_connection_idle,
453 Duration::from_secs(30 * 60)
454 );
455 assert_eq!(
456 cfg.keepalive.max_connection_age,
457 Duration::from_secs(90 * 60)
458 );
459 assert_eq!(
460 cfg.keepalive.max_connection_age_grace,
461 Duration::from_secs(15)
462 );
463 assert_eq!(cfg.keepalive.time, Duration::from_secs(5));
464 assert_eq!(cfg.keepalive.timeout, Duration::from_secs(2));
465 }
466
467 #[test]
468 fn test_invalid_keepalive_duration_strings_fail_deserialize() {
469 let invalid_json_cases = [
470 r#"{ "keepalive": { "time": "zz" } }"#,
471 r#"{ "keepalive": { "timeout": "-5s" } }"#,
472 r#"{ "keepalive": { "max_connection_age": "10x" } }"#,
473 ];
474 for js in invalid_json_cases {
475 let res: Result<ServerConfig, _> = serde_json::from_str(js);
476 assert!(res.is_err(), "expected error for json: {}", js);
477 }
478 }
479
480 #[test]
481 fn test_server_config_keepalive_roundtrip_duration_serialization() {
482 let keepalive = KeepaliveServerParameters {
483 max_connection_idle: Duration::from_secs(10).into(),
484 max_connection_age: Duration::from_secs(20).into(),
485 max_connection_age_grace: Duration::from_secs(30).into(),
486 time: Duration::from_secs(3).into(),
487 timeout: Duration::from_secs(1).into(),
488 };
489
490 let cfg = ServerConfig::with_endpoint("127.0.0.1:50000").with_keepalive(keepalive.clone());
491 let serialized = serde_json::to_string(&cfg).expect("serialize");
492 let deserialized: ServerConfig = serde_json::from_str(&serialized).expect("deserialize");
493
494 assert_eq!(
495 deserialized.keepalive.max_connection_idle,
496 Duration::from_secs(10)
497 );
498 assert_eq!(
499 deserialized.keepalive.max_connection_age,
500 Duration::from_secs(20)
501 );
502 assert_eq!(
503 deserialized.keepalive.max_connection_age_grace,
504 Duration::from_secs(30)
505 );
506 assert_eq!(deserialized.keepalive.time, Duration::from_secs(3));
507 assert_eq!(deserialized.keepalive.timeout, Duration::from_secs(1));
508 }
509}