1use dashmap::DashMap;
2use siphon_protocol::{ServerMessage, TunnelType};
3use std::sync::Arc;
4use tokio::sync::mpsc;
5
6pub struct TunnelHandle {
8 pub sender: mpsc::Sender<ServerMessage>,
10 #[allow(dead_code)]
12 pub client_id: String,
13 #[allow(dead_code)]
15 pub tunnel_type: TunnelType,
16 pub dns_record_id: Option<String>,
18}
19
20pub struct Router {
22 routes: DashMap<String, TunnelHandle>,
24 tcp_ports: DashMap<u16, String>,
26}
27
28impl Router {
29 pub fn new() -> Arc<Self> {
30 Arc::new(Self {
31 routes: DashMap::new(),
32 tcp_ports: DashMap::new(),
33 })
34 }
35
36 pub fn register(
38 &self,
39 subdomain: String,
40 handle: TunnelHandle,
41 tcp_port: Option<u16>,
42 ) -> Result<(), RouterError> {
43 if self.routes.contains_key(&subdomain) {
45 return Err(RouterError::SubdomainTaken(subdomain));
46 }
47
48 if let Some(port) = tcp_port {
50 self.tcp_ports.insert(port, subdomain.clone());
51 }
52
53 self.routes.insert(subdomain, handle);
54 Ok(())
55 }
56
57 pub fn unregister(&self, subdomain: &str) -> Option<TunnelHandle> {
59 if let Some((_, handle)) = self.routes.remove(subdomain) {
60 self.tcp_ports.retain(|_, v| v != subdomain);
62 Some(handle)
63 } else {
64 None
65 }
66 }
67
68 pub fn get_sender(&self, subdomain: &str) -> Option<mpsc::Sender<ServerMessage>> {
70 self.routes.get(subdomain).map(|h| h.sender.clone())
71 }
72
73 #[allow(dead_code)]
75 pub fn get_subdomain_for_port(&self, port: u16) -> Option<String> {
76 self.tcp_ports.get(&port).map(|s| s.clone())
77 }
78
79 pub fn is_available(&self, subdomain: &str) -> bool {
81 !self.routes.contains_key(subdomain)
82 }
83
84 #[allow(dead_code)]
86 pub fn list_subdomains(&self) -> Vec<String> {
87 self.routes.iter().map(|r| r.key().clone()).collect()
88 }
89}
90
91impl Default for Router {
92 fn default() -> Self {
93 Self {
94 routes: DashMap::new(),
95 tcp_ports: DashMap::new(),
96 }
97 }
98}
99
100#[derive(Debug, thiserror::Error)]
101pub enum RouterError {
102 #[error("Subdomain already taken: {0}")]
103 SubdomainTaken(String),
104}