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
63 pub port:u16,
64
65 pub tls_port:Option<u16>,
66
67 pub use_tls:bool,
68
69 pub health_check_path:Option<String>,
70}
71
72impl LocalService {
73 pub fn get_port(&self) -> u16 {
75 if self.use_tls {
76 self.tls_port.unwrap_or_else(|| self.port + 1000)
77 } else {
78 self.port
79 }
80 }
81}
82
83#[derive(Clone)]
89pub struct ServiceRegistry {
90 services:Arc<RwLock<HashMap<String, LocalService>>>,
92
93 cert_manager:Option<std::sync::Arc<std::sync::Mutex<super::CertificateManager::CertificateManager>>>,
95}
96
97impl ServiceRegistry {
98 pub fn new() -> Self {
102 dev_log!("lifecycle", "[ServiceRegistry] Creating new ServiceRegistry");
103
104 Self { services:Arc::new(RwLock::new(HashMap::new())), cert_manager:None }
105 }
106
107 pub fn with_tls(
116 cert_manager:std::sync::Arc<std::sync::Mutex<super::CertificateManager::CertificateManager>>,
117 ) -> Self {
118 dev_log!("lifecycle", "[ServiceRegistry] Creating new ServiceRegistry with TLS support");
119
120 Self { services:Arc::new(RwLock::new(HashMap::new())), cert_manager:Some(cert_manager) }
121 }
122
123 pub fn register(&self, name:String, port:u16, health_check_path:Option<String>) {
138 self.register_with_options(name, port, None, false, health_check_path);
139 }
140
141 pub fn register_with_options(
164 &self,
165
166 name:String,
167
168 port:u16,
169
170 tls_port:Option<u16>,
171
172 use_tls:bool,
173
174 health_check_path:Option<String>,
175 ) {
176 dev_log!(
177 "lifecycle",
178 "[ServiceRegistry] Registering service: {} -> HTTP:{}, TLS:{}, use_tls:{}",
179 name,
180 port,
181 tls_port.unwrap_or(port + 1000),
182 use_tls
183 );
184
185 let service = LocalService { name:name.clone(), port, tls_port, use_tls, health_check_path };
186
187 if use_tls {
189 if let Some(cert_manager) = &self.cert_manager {
190 dev_log!("lifecycle", "[ServiceRegistry] TLS will be provisioned on-demand for {}", name);
192 } else {
193 dev_log!(
194 "lifecycle",
195 "warn: [ServiceRegistry] Service {} requested TLS but no certificate manager available",
196 name
197 );
198 }
199 }
200
201 if let Ok(mut services) = self.services.write() {
202 if services.contains_key(&name) {
204 dev_log!(
205 "lifecycle",
206 "warn: [ServiceRegistry] Service {} already registered, overwriting",
207 name
208 );
209 }
210
211 services.insert(name.clone(), service);
212
213 dev_log!("lifecycle", "[ServiceRegistry] Service {} registered successfully", name);
214 } else {
215 dev_log!(
216 "lifecycle",
217 "error: [ServiceRegistry] Failed to acquire write lock for registration"
218 );
219 }
220 }
221
222 pub fn lookup(&self, name:&str) -> Option<LocalService> {
242 dev_log!("lifecycle", "[ServiceRegistry] Looking up service: {}", name);
243
244 if let Ok(services) = self.services.read() {
245 let service = services.get(name).cloned();
246
247 if service.is_some() {
248 dev_log!("lifecycle", "[ServiceRegistry] Service {} found", name);
249 } else {
250 dev_log!("lifecycle", "[ServiceRegistry] Service {} not found", name);
251 }
252
253 service
254 } else {
255 dev_log!("lifecycle", "error: [ServiceRegistry] Failed to acquire read lock for lookup");
256
257 None
258 }
259 }
260
261 pub fn all_services(&self) -> Vec<LocalService> {
267 if let Ok(services) = self.services.read() {
268 services.values().cloned().collect()
269 } else {
270 dev_log!(
271 "lifecycle",
272 "error: [ServiceRegistry] Failed to acquire read lock for all_services"
273 );
274
275 Vec::new()
276 }
277 }
278
279 pub async fn health_check(&self, name:&str) -> Result<bool, Box<dyn std::error::Error + Send + Sync>> {
291 let service = self.lookup(name).ok_or_else(|| format!("Service {} not found", name))?;
292
293 let health_path = service.health_check_path.as_deref().unwrap_or("/health");
294
295 let addr = format!("127.0.0.1:{}", service.port);
296
297 dev_log!(
298 "lifecycle",
299 "[ServiceRegistry] Performing health check for {} at {}:{}",
300 name,
301 addr,
302 health_path
303 );
304
305 match TcpStream::connect(&addr).await {
307 Ok(mut stream) => {
308 let request = format!("GET {} HTTP/1.1\r\nHost: 127.0.0.1:{}\r\n\r\n", health_path, service.port);
310
311 match stream.write_all(request.as_bytes()).await {
312 Ok(_) => {
313 let mut buffer = [0u8; 1024];
315
316 match stream.read(&mut buffer).await {
317 Ok(n) => {
318 let response = String::from_utf8_lossy(&buffer[..n]);
319
320 let is_healthy = response.contains("HTTP/1.1 200") || response.contains("HTTP/1.0 200");
321
322 if is_healthy {
323 dev_log!("lifecycle", "[ServiceRegistry] Service {} is healthy", name);
324 } else {
325 dev_log!(
326 "lifecycle",
327 "warn: [ServiceRegistry] Service {} health check failed: not 200",
328 name
329 );
330 }
331
332 Ok(is_healthy)
333 },
334
335 Err(e) => {
336 dev_log!(
337 "lifecycle",
338 "warn: [ServiceRegistry] Service {} health check failed to read: {}",
339 name,
340 e
341 );
342
343 Ok(false)
344 },
345 }
346 },
347
348 Err(e) => {
349 dev_log!(
350 "lifecycle",
351 "warn: [ServiceRegistry] Service {} health check failed to write: {}",
352 name,
353 e
354 );
355
356 Ok(false)
357 },
358 }
359 },
360
361 Err(e) => {
362 dev_log!(
363 "lifecycle",
364 "warn: [ServiceRegistry] Service {} health check failed to connect: {}",
365 name,
366 e
367 );
368
369 Ok(false)
370 },
371 }
372 }
373
374 pub fn unregister(&self, name:&str) -> Option<LocalService> {
385 dev_log!("lifecycle", "[ServiceRegistry] Unregistering service: {}", name);
386
387 if let Ok(mut services) = self.services.write() {
388 services.remove(name)
389 } else {
390 dev_log!(
391 "lifecycle",
392 "error: [ServiceRegistry] Failed to acquire write lock for unregistration"
393 );
394
395 None
396 }
397 }
398
399 pub async fn get_tls_config(&self, name:&str) -> Option<std::sync::Arc<rustls::ServerConfig>> {
412 let service = self.lookup(name)?;
413
414 if !service.use_tls {
415 return None;
416 }
417
418 let cert_manager = self.cert_manager.as_ref()?;
419
420 let manager = cert_manager
421 .lock()
422 .map_err(|e| {
423 dev_log!("lifecycle", "error: [ServiceRegistry] Failed to acquire lock: {}", e);
424 })
425 .ok()?;
426
427 manager.build_server_config(name).await.ok()
428 }
429
430 pub fn uses_tls(&self, name:&str) -> bool { self.lookup(name).map(|s| s.use_tls).unwrap_or(false) }
440}
441
442impl Default for ServiceRegistry {
443 fn default() -> Self { Self::new() }
444}
445
446#[cfg(test)]
447mod tests {
448
449 use super::*;
450
451 #[test]
452 fn test_register_and_lookup() {
453 let registry = ServiceRegistry::new();
454
455 registry.register("test.service.land".to_string(), 8080, Some("/health".to_string()));
456
457 let service = registry.lookup("test.service.land").unwrap();
458
459 assert_eq!(service.name, "test.service.land");
460
461 assert_eq!(service.port, 8080);
462
463 assert_eq!(service.health_check_path, Some("/health".to_string()));
464 }
465
466 #[test]
467 fn test_lookup_nonexistent() {
468 let registry = ServiceRegistry::new();
469
470 let service = registry.lookup("nonexistent.service.land");
471
472 assert!(service.is_none());
473 }
474
475 #[test]
476 fn test_all_services() {
477 let registry = ServiceRegistry::new();
478
479 registry.register("service1.land".to_string(), 8080, None);
480
481 registry.register("service2.land".to_string(), 8081, None);
482
483 let services = registry.all_services();
484
485 assert_eq!(services.len(), 2);
486 }
487
488 #[test]
489 fn test_unregister() {
490 let registry = ServiceRegistry::new();
491
492 registry.register("test.service.land".to_string(), 8080, None);
493
494 assert!(registry.lookup("test.service.land").is_some());
495
496 registry.unregister("test.service.land");
497
498 assert!(registry.lookup("test.service.land").is_none());
499 }
500
501 #[test]
502 fn test_overwrite_registration() {
503 let registry = ServiceRegistry::new();
504
505 registry.register("test.service.land".to_string(), 8080, None);
506
507 registry.register("test.service.land".to_string(), 9090, None);
508
509 let service = registry.lookup("test.service.land").unwrap();
510
511 assert_eq!(service.port, 9090);
512 }
513
514 #[test]
515 fn test_tls_service() {
516 let registry = ServiceRegistry::new();
517
518 registry.register_with_options(
519 "secure.service.land".to_string(),
520 8080,
521 Some(8443),
522 true,
523 Some("/health".to_string()),
524 );
525
526 let service = registry.lookup("secure.service.land").unwrap();
527
528 assert_eq!(service.name, "secure.service.land");
529
530 assert_eq!(service.port, 8080);
531
532 assert_eq!(service.tls_port, Some(8443));
533
534 assert_eq!(service.use_tls, true);
535
536 assert_eq!(service.get_port(), 8443);
537 }
538
539 #[test]
540 fn test_default_tls_port() {
541 let registry = ServiceRegistry::new();
542
543 registry.register_with_options(
544 "secure.service.land".to_string(),
545 8080,
546 None, true,
548 None,
549 );
550
551 let service = registry.lookup("secure.service.land").unwrap();
552
553 assert_eq!(service.tls_port, None); assert_eq!(service.get_port(), 9080); }
556}