1use std::sync::Arc;
4use std::time::Duration;
5
6use xds_cache::ShardedCache;
7use xds_core::{ResourceRegistry, XdsError, XdsResult};
8
9use crate::config::{CompressionConfig, ServerConfig};
10use crate::connections::{ConnectionLimits, ConnectionTracker};
11use crate::metrics::XdsMetrics;
12use crate::shutdown::ShutdownController;
13use crate::XdsServer;
14
15#[must_use = "builder is unused unless `.build()` is called"]
39#[derive(Debug, Default)]
40pub struct XdsServerBuilder {
41 cache: Option<Arc<ShardedCache>>,
42 registry: Option<Arc<ResourceRegistry>>,
43 enable_sotw: bool,
44 enable_delta: bool,
45 max_concurrent_streams: Option<u32>,
46 keepalive_interval: Option<Duration>,
47 keepalive_timeout: Option<Duration>,
48 max_request_size: Option<usize>,
49 compression: Option<CompressionConfig>,
50 enable_health: bool,
52 enable_metrics: bool,
53 enable_connection_tracking: bool,
54 grace_period: Option<Duration>,
55 connection_limits: Option<ConnectionLimits>,
56 #[cfg(feature = "tls")]
58 tls_config: Option<tonic::transport::ServerTlsConfig>,
59}
60
61impl XdsServerBuilder {
62 pub fn new() -> Self {
64 Self {
65 enable_sotw: true, enable_health: true, enable_metrics: true, enable_connection_tracking: true, ..Default::default()
70 }
71 }
72
73 pub fn cache(mut self, cache: Arc<ShardedCache>) -> Self {
77 self.cache = Some(cache);
78 self
79 }
80
81 pub fn registry(mut self, registry: Arc<ResourceRegistry>) -> Self {
85 self.registry = Some(registry);
86 self
87 }
88
89 pub fn enable_sotw(mut self) -> Self {
91 self.enable_sotw = true;
92 self
93 }
94
95 pub fn disable_sotw(mut self) -> Self {
97 self.enable_sotw = false;
98 self
99 }
100
101 pub fn enable_delta(mut self) -> Self {
103 self.enable_delta = true;
104 self
105 }
106
107 pub fn disable_delta(mut self) -> Self {
109 self.enable_delta = false;
110 self
111 }
112
113 pub fn max_concurrent_streams(mut self, max: u32) -> Self {
115 self.max_concurrent_streams = Some(max);
116 self
117 }
118
119 pub fn keepalive_interval(mut self, interval: Duration) -> Self {
121 self.keepalive_interval = Some(interval);
122 self
123 }
124
125 pub fn keepalive_timeout(mut self, timeout: Duration) -> Self {
127 self.keepalive_timeout = Some(timeout);
128 self
129 }
130
131 pub fn max_request_size(mut self, size: usize) -> Self {
133 self.max_request_size = Some(size);
134 self
135 }
136
137 pub fn enable_compression(mut self) -> Self {
139 self.compression = Some(CompressionConfig::gzip());
140 self
141 }
142
143 pub fn compression(mut self, config: CompressionConfig) -> Self {
145 self.compression = Some(config);
146 self
147 }
148
149 pub fn enable_health_check(mut self) -> Self {
153 self.enable_health = true;
154 self
155 }
156
157 pub fn disable_health_check(mut self) -> Self {
159 self.enable_health = false;
160 self
161 }
162
163 pub fn enable_metrics(mut self) -> Self {
165 self.enable_metrics = true;
166 self
167 }
168
169 pub fn disable_metrics(mut self) -> Self {
171 self.enable_metrics = false;
172 self
173 }
174
175 pub fn enable_connection_tracking(mut self) -> Self {
177 self.enable_connection_tracking = true;
178 self
179 }
180
181 pub fn disable_connection_tracking(mut self) -> Self {
183 self.enable_connection_tracking = false;
184 self
185 }
186
187 pub fn graceful_shutdown(mut self, grace_period: Duration) -> Self {
195 self.grace_period = Some(grace_period);
196 self
197 }
198
199 pub fn connection_limits(mut self, limits: ConnectionLimits) -> Self {
203 self.connection_limits = Some(limits);
204 self
205 }
206
207 pub fn max_connections(mut self, max: u64) -> Self {
209 let limits = self.connection_limits.get_or_insert_with(ConnectionLimits::default);
210 limits.max_connections = max;
211 self
212 }
213
214 pub fn max_connections_per_ip(mut self, max: u64) -> Self {
216 let limits = self.connection_limits.get_or_insert_with(ConnectionLimits::default);
217 limits.max_per_ip = max;
218 self
219 }
220
221 #[cfg(feature = "tls")]
250 #[cfg_attr(docsrs, doc(cfg(feature = "tls")))]
251 pub fn tls_config(mut self, config: tonic::transport::ServerTlsConfig) -> Self {
252 self.tls_config = Some(config);
253 self
254 }
255
256 pub fn build(self) -> XdsResult<XdsServer> {
264 let cache = self
265 .cache
266 .ok_or_else(|| XdsError::Configuration("cache is required".into()))?;
267
268 if !self.enable_sotw && !self.enable_delta {
269 return Err(XdsError::Configuration(
270 "at least one protocol (SotW or Delta) must be enabled".into(),
271 ));
272 }
273
274 let registry = self
275 .registry
276 .unwrap_or_else(|| Arc::new(ResourceRegistry::new()));
277
278 let config = ServerConfig {
279 enable_sotw: self.enable_sotw,
280 enable_delta: self.enable_delta,
281 max_concurrent_streams: self.max_concurrent_streams.or(Some(100)),
282 keepalive_interval: self.keepalive_interval.or(Some(Duration::from_secs(30))),
283 keepalive_timeout: self.keepalive_timeout.or(Some(Duration::from_secs(10))),
284 max_request_size: self.max_request_size.unwrap_or(4 * 1024 * 1024),
285 compression: self.compression.unwrap_or_default(),
286 grace_period: self.grace_period.unwrap_or(Duration::from_secs(30)),
287 enable_health: self.enable_health,
288 enable_metrics: self.enable_metrics,
289 enable_connection_tracking: self.enable_connection_tracking,
290 };
291
292 let metrics = if self.enable_metrics {
294 Some(XdsMetrics::new())
295 } else {
296 None
297 };
298
299 let connections = if self.enable_connection_tracking {
300 let limits = self.connection_limits.unwrap_or_default();
301 Some(ConnectionTracker::new(limits))
302 } else {
303 None
304 };
305
306 let shutdown = ShutdownController::new();
307
308 Ok(XdsServer {
309 cache,
310 registry,
311 config,
312 metrics,
313 shutdown,
314 connections,
315 #[cfg(feature = "tls")]
316 tls_config: self.tls_config,
317 })
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn builder_requires_cache() {
327 let result = XdsServerBuilder::new().build();
328 assert!(result.is_err());
329 }
330
331 #[test]
332 fn builder_requires_protocol() {
333 let cache = Arc::new(ShardedCache::new());
334 let result = XdsServerBuilder::new()
335 .cache(cache)
336 .disable_sotw()
337 .disable_delta()
338 .build();
339 assert!(result.is_err());
340 }
341
342 #[test]
343 fn builder_success() {
344 let cache = Arc::new(ShardedCache::new());
345 let server = XdsServerBuilder::new()
346 .cache(cache)
347 .enable_sotw()
348 .enable_delta()
349 .max_concurrent_streams(200)
350 .build()
351 .expect("server should build successfully");
352
353 assert!(server.config().enable_sotw);
354 assert!(server.config().enable_delta);
355 assert_eq!(server.config().max_concurrent_streams, Some(200));
356 }
357
358 #[cfg(feature = "tls")]
359 #[test]
360 fn builder_accepts_tls_config() {
361 use tonic::transport::ServerTlsConfig;
362
363 let cache = Arc::new(ShardedCache::new());
364 let tls = ServerTlsConfig::new();
365
366 let server = XdsServerBuilder::new()
367 .cache(cache)
368 .tls_config(tls)
369 .build()
370 .expect("server should build with TLS config");
371
372 assert!(server.config().enable_sotw);
376 }
377}