1use std::{
36 collections::HashMap,
37 sync::{Arc, RwLock},
38};
39
40#[allow(unused_imports)]
41use http::{Request as HttpRequest, Response as HttpResponse, header};
42use tokio::{
43 io::{AsyncReadExt, AsyncWriteExt},
44 net::TcpStream,
45};
46
47use crate::dev_log;
48
49#[derive(Debug, Clone)]
60pub struct LocalService {
61 pub name:String,
62 pub port:u16,
63 pub tls_port:Option<u16>,
64 pub use_tls:bool,
65 pub health_check_path:Option<String>,
66}
67
68impl LocalService {
69 pub fn get_port(&self) -> u16 {
71 if self.use_tls {
72 self.tls_port.unwrap_or_else(|| self.port + 1000)
73 } else {
74 self.port
75 }
76 }
77}
78
79#[derive(Clone)]
85pub struct ServiceRegistry {
86 services:Arc<RwLock<HashMap<String, LocalService>>>,
88 cert_manager:Option<std::sync::Arc<std::sync::Mutex<super::CertificateManager::CertificateManager>>>,
90}
91
92impl ServiceRegistry {
93 pub fn new() -> Self {
97 dev_log!("lifecycle", "[ServiceRegistry] Creating new ServiceRegistry");
98 Self { services:Arc::new(RwLock::new(HashMap::new())), cert_manager:None }
99 }
100
101 pub fn with_tls(
110 cert_manager:std::sync::Arc<std::sync::Mutex<super::CertificateManager::CertificateManager>>,
111 ) -> Self {
112 dev_log!("lifecycle", "[ServiceRegistry] Creating new ServiceRegistry with TLS support");
113 Self { services:Arc::new(RwLock::new(HashMap::new())), cert_manager:Some(cert_manager) }
114 }
115
116 pub fn register(&self, name:String, port:u16, health_check_path:Option<String>) {
131 self.register_with_options(name, port, None, false, health_check_path);
132 }
133
134 pub fn register_with_options(
157 &self,
158 name:String,
159 port:u16,
160 tls_port:Option<u16>,
161 use_tls:bool,
162 health_check_path:Option<String>,
163 ) {
164 dev_log!(
165 "lifecycle",
166 "[ServiceRegistry] Registering service: {} -> HTTP:{}, TLS:{}, use_tls:{}",
167 name,
168 port,
169 tls_port.unwrap_or(port + 1000),
170 use_tls
171 );
172
173 let service = LocalService { name:name.clone(), port, tls_port, use_tls, health_check_path };
174
175 if use_tls {
177 if let Some(cert_manager) = &self.cert_manager {
178 dev_log!("lifecycle", "[ServiceRegistry] TLS will be provisioned on-demand for {}", name);
180 } else {
181 dev_log!(
182 "lifecycle",
183 "warn: [ServiceRegistry] Service {} requested TLS but no certificate manager available",
184 name
185 );
186 }
187 }
188
189 if let Ok(mut services) = self.services.write() {
190 if services.contains_key(&name) {
192 dev_log!(
193 "lifecycle",
194 "warn: [ServiceRegistry] Service {} already registered, overwriting",
195 name
196 );
197 }
198 services.insert(name.clone(), service);
199 dev_log!("lifecycle", "[ServiceRegistry] Service {} registered successfully", name);
200 } else {
201 dev_log!(
202 "lifecycle",
203 "error: [ServiceRegistry] Failed to acquire write lock for registration"
204 );
205 }
206 }
207
208 pub fn lookup(&self, name:&str) -> Option<LocalService> {
228 dev_log!("lifecycle", "[ServiceRegistry] Looking up service: {}", name);
229
230 if let Ok(services) = self.services.read() {
231 let service = services.get(name).cloned();
232 if service.is_some() {
233 dev_log!("lifecycle", "[ServiceRegistry] Service {} found", name);
234 } else {
235 dev_log!("lifecycle", "[ServiceRegistry] Service {} not found", name);
236 }
237 service
238 } else {
239 dev_log!("lifecycle", "error: [ServiceRegistry] Failed to acquire read lock for lookup");
240 None
241 }
242 }
243
244 pub fn all_services(&self) -> Vec<LocalService> {
250 if let Ok(services) = self.services.read() {
251 services.values().cloned().collect()
252 } else {
253 dev_log!(
254 "lifecycle",
255 "error: [ServiceRegistry] Failed to acquire read lock for all_services"
256 );
257 Vec::new()
258 }
259 }
260
261 pub async fn health_check(&self, name:&str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
273 let service = self.lookup(name).ok_or_else(|| format!("Service {} not found", name))?;
274
275 let health_path = service.health_check_path.as_deref().unwrap_or("/health");
276 let addr = format!("127.0.0.1:{}", service.port);
277
278 dev_log!(
279 "lifecycle",
280 "[ServiceRegistry] Performing health check for {} at {}:{}",
281 name,
282 addr,
283 health_path
284 );
285
286 match TcpStream::connect(&addr).await {
288 Ok(mut stream) => {
289 let request = format!("GET {} HTTP/1.1\r\nHost: 127.0.0.1:{}\r\n\r\n", health_path, service.port);
291
292 match stream.write_all(request.as_bytes()).await {
293 Ok(_) => {
294 let mut buffer = [0u8; 1024];
296 match stream.read(&mut buffer).await {
297 Ok(n) => {
298 let response = String::from_utf8_lossy(&buffer[..n]);
299 let is_healthy = response.contains("HTTP/1.1 200") || response.contains("HTTP/1.0 200");
300 if is_healthy {
301 dev_log!("lifecycle", "[ServiceRegistry] Service {} is healthy", name);
302 } else {
303 dev_log!(
304 "lifecycle",
305 "warn: [ServiceRegistry] Service {} health check failed: not 200",
306 name
307 );
308 }
309 Ok(is_healthy)
310 },
311 Err(e) => {
312 dev_log!(
313 "lifecycle",
314 "warn: [ServiceRegistry] Service {} health check failed to read: {}",
315 name,
316 e
317 );
318 Ok(false)
319 },
320 }
321 },
322 Err(e) => {
323 dev_log!(
324 "lifecycle",
325 "warn: [ServiceRegistry] Service {} health check failed to write: {}",
326 name,
327 e
328 );
329 Ok(false)
330 },
331 }
332 },
333 Err(e) => {
334 dev_log!(
335 "lifecycle",
336 "warn: [ServiceRegistry] Service {} health check failed to connect: {}",
337 name,
338 e
339 );
340 Ok(false)
341 },
342 }
343 }
344
345 pub fn unregister(&self, name:&str) -> Option<LocalService> {
356 dev_log!("lifecycle", "[ServiceRegistry] Unregistering service: {}", name);
357
358 if let Ok(mut services) = self.services.write() {
359 services.remove(name)
360 } else {
361 dev_log!(
362 "lifecycle",
363 "error: [ServiceRegistry] Failed to acquire write lock for unregistration"
364 );
365 None
366 }
367 }
368
369 pub async fn get_tls_config(&self, name:&str) -> Option<std::sync::Arc<rustls::ServerConfig>> {
382 let service = self.lookup(name)?;
383
384 if !service.use_tls {
385 return None;
386 }
387
388 let cert_manager = self.cert_manager.as_ref()?;
389 let manager = cert_manager
390 .lock()
391 .map_err(|e| {
392 dev_log!("lifecycle", "error: [ServiceRegistry] Failed to acquire lock: {}", e);
393 })
394 .ok()?;
395 manager.build_server_config(name).await.ok()
396 }
397
398 pub fn uses_tls(&self, name:&str) -> bool { self.lookup(name).map(|s| s.use_tls).unwrap_or(false) }
408}
409
410impl Default for ServiceRegistry {
411 fn default() -> Self { Self::new() }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn test_register_and_lookup() {
420 let registry = ServiceRegistry::new();
421
422 registry.register("test.service.land".to_string(), 8080, Some("/health".to_string()));
423
424 let service = registry.lookup("test.service.land").unwrap();
425 assert_eq!(service.name, "test.service.land");
426 assert_eq!(service.port, 8080);
427 assert_eq!(service.health_check_path, Some("/health".to_string()));
428 }
429
430 #[test]
431 fn test_lookup_nonexistent() {
432 let registry = ServiceRegistry::new();
433
434 let service = registry.lookup("nonexistent.service.land");
435 assert!(service.is_none());
436 }
437
438 #[test]
439 fn test_all_services() {
440 let registry = ServiceRegistry::new();
441
442 registry.register("service1.land".to_string(), 8080, None);
443 registry.register("service2.land".to_string(), 8081, None);
444
445 let services = registry.all_services();
446 assert_eq!(services.len(), 2);
447 }
448
449 #[test]
450 fn test_unregister() {
451 let registry = ServiceRegistry::new();
452
453 registry.register("test.service.land".to_string(), 8080, None);
454 assert!(registry.lookup("test.service.land").is_some());
455
456 registry.unregister("test.service.land");
457 assert!(registry.lookup("test.service.land").is_none());
458 }
459
460 #[test]
461 fn test_overwrite_registration() {
462 let registry = ServiceRegistry::new();
463
464 registry.register("test.service.land".to_string(), 8080, None);
465 registry.register("test.service.land".to_string(), 9090, None);
466
467 let service = registry.lookup("test.service.land").unwrap();
468 assert_eq!(service.port, 9090);
469 }
470
471 #[test]
472 fn test_tls_service() {
473 let registry = ServiceRegistry::new();
474
475 registry.register_with_options(
476 "secure.service.land".to_string(),
477 8080,
478 Some(8443),
479 true,
480 Some("/health".to_string()),
481 );
482
483 let service = registry.lookup("secure.service.land").unwrap();
484 assert_eq!(service.name, "secure.service.land");
485 assert_eq!(service.port, 8080);
486 assert_eq!(service.tls_port, Some(8443));
487 assert_eq!(service.use_tls, true);
488 assert_eq!(service.get_port(), 8443);
489 }
490
491 #[test]
492 fn test_default_tls_port() {
493 let registry = ServiceRegistry::new();
494
495 registry.register_with_options(
496 "secure.service.land".to_string(),
497 8080,
498 None, true,
500 None,
501 );
502
503 let service = registry.lookup("secure.service.land").unwrap();
504 assert_eq!(service.tls_port, None); assert_eq!(service.get_port(), 9080); }
507}