camel_component_ws/
config.rs1use std::time::Duration;
2
3use camel_component_api::CamelError;
4
5#[derive(Debug, Clone, Default, serde::Deserialize)]
6pub struct WsConfig {
7 pub max_connections: Option<u32>,
8 pub max_message_size: Option<u32>,
9 pub heartbeat_interval_ms: Option<u64>,
10 pub idle_timeout_ms: Option<u64>,
11 pub connect_timeout_ms: Option<u64>,
12 pub response_timeout_ms: Option<u64>,
13}
14
15#[derive(Debug, Clone)]
16pub struct WsEndpointConfig {
17 pub scheme: String,
18 pub host: String,
19 pub port: u16,
20 pub path: String,
21 pub max_connections: u32,
22 pub max_message_size: u32,
23 pub send_to_all: bool,
24 pub heartbeat_interval: Duration,
25 pub idle_timeout: Duration,
26 pub connect_timeout: Duration,
27 pub response_timeout: Duration,
28 pub allow_origin: String,
29 pub tls_cert: Option<String>,
30 pub tls_key: Option<String>,
31}
32
33impl Default for WsEndpointConfig {
34 fn default() -> Self {
35 Self {
36 scheme: "ws".into(),
37 host: "0.0.0.0".into(),
38 port: 8080,
39 path: "/".into(),
40 max_connections: 100,
41 max_message_size: 65536,
42 send_to_all: false,
43 heartbeat_interval: Duration::ZERO,
44 idle_timeout: Duration::ZERO,
45 connect_timeout: Duration::from_secs(10),
46 response_timeout: Duration::from_secs(30),
47 allow_origin: "*".into(),
48 tls_cert: None,
49 tls_key: None,
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
55pub struct WsServerConfig {
56 pub inner: WsEndpointConfig,
57}
58
59#[derive(Debug, Clone)]
60pub struct WsClientConfig {
61 pub inner: WsEndpointConfig,
62}
63
64impl WsEndpointConfig {
65 pub fn from_uri(uri: &str) -> Result<Self, CamelError> {
66 let parsed = camel_component_api::parse_uri(uri)
67 .map_err(|e| CamelError::EndpointCreationFailed(e.to_string()))?;
68
69 let scheme = parsed.scheme;
70 if scheme != "ws" && scheme != "wss" {
71 return Err(CamelError::EndpointCreationFailed(format!(
72 "Invalid WebSocket scheme: {scheme}"
73 )));
74 }
75
76 let host_port_path = parsed.path;
77 let host_port_path = host_port_path.strip_prefix("//").unwrap_or(&host_port_path);
78 let (host_port, path) = match host_port_path.split_once('/') {
79 Some((hp, p)) => (hp, format!("/{p}")),
80 None => (host_port_path, "/".to_string()),
81 };
82
83 let (host, port) = match host_port.rsplit_once(':') {
84 Some((h, p)) if p.parse::<u16>().is_ok() => {
85 let parsed_port = p.parse::<u16>().unwrap(); (h.to_string(), parsed_port)
87 }
88 _ => (
89 host_port.to_string(),
90 if scheme == "wss" { 443 } else { 80 },
91 ),
92 };
93
94 let mut cfg = Self {
95 scheme,
96 host: if host.is_empty() {
97 "0.0.0.0".to_string()
98 } else {
99 host
100 },
101 port,
102 path,
103 ..Self::default()
104 };
105
106 let params = parsed.params;
107 if let Some(v) = params
109 .get("maxConnections")
110 .and_then(|v| v.parse::<u32>().ok())
111 {
112 if v == 0 {
113 return Err(CamelError::InvalidUri("maxConnections must be >= 1".into()));
114 }
115 cfg.max_connections = v;
116 }
117 if let Some(v) = params
119 .get("maxMessageSize")
120 .and_then(|v| v.parse::<u32>().ok())
121 {
122 if v == 0 {
123 return Err(CamelError::InvalidUri("maxMessageSize must be > 0".into()));
124 }
125 cfg.max_message_size = v;
126 }
127 if let Some(v) = params.get("sendToAll").and_then(|v| v.parse::<bool>().ok()) {
128 cfg.send_to_all = v;
129 }
130 if let Some(v) = params
131 .get("heartbeatIntervalMs")
132 .and_then(|v| v.parse::<u64>().ok())
133 {
134 cfg.heartbeat_interval = Duration::from_millis(v);
135 }
136 if let Some(v) = params
137 .get("idleTimeoutMs")
138 .and_then(|v| v.parse::<u64>().ok())
139 {
140 cfg.idle_timeout = Duration::from_millis(v);
141 }
142 if let Some(v) = params
143 .get("connectTimeoutMs")
144 .and_then(|v| v.parse::<u64>().ok())
145 {
146 cfg.connect_timeout = Duration::from_millis(v);
147 }
148 if let Some(v) = params
149 .get("responseTimeoutMs")
150 .and_then(|v| v.parse::<u64>().ok())
151 {
152 cfg.response_timeout = Duration::from_millis(v);
153 }
154 if let Some(v) = params.get("allowOrigin") {
155 if v.is_empty() {
156 return Err(CamelError::InvalidUri(
157 "allowOrigin must not be empty when specified".into(),
158 ));
159 }
160 cfg.allow_origin = v.to_string();
161 }
162 if let Some(v) = params.get("tlsCert") {
163 cfg.tls_cert = Some(v.to_string());
164 }
165 if let Some(v) = params.get("tlsKey") {
166 cfg.tls_key = Some(v.to_string());
167 }
168
169 Ok(cfg)
170 }
171
172 pub fn server_config(&self) -> WsServerConfig {
173 WsServerConfig {
174 inner: self.clone(),
175 }
176 }
177
178 pub fn client_config(&self) -> WsClientConfig {
179 WsClientConfig {
180 inner: self.clone(),
181 }
182 }
183
184 pub fn canonical_host(&self) -> String {
185 match self.host.as_str() {
186 "0.0.0.0" | "localhost" => "127.0.0.1".to_string(),
187 h => h.to_string(),
188 }
189 }
190}