Connect a WebSocket. Idempotent: won't reconnect if already open.
(
id: &str,
url: &str,
f: impl FnOnce(&mut WsConfig) -> &mut WsConfig,
)
| 245 | |
| 246 | /// Connect a WebSocket. Idempotent: won't reconnect if already open. |
| 247 | pub fn ws_connect( |
| 248 | id: &str, |
| 249 | url: &str, |
| 250 | f: impl FnOnce(&mut WsConfig) -> &mut WsConfig, |
| 251 | ) { |
| 252 | let key = hash_id(id); |
| 253 | let mut mgr = NET_MANAGER.lock().unwrap(); |
| 254 | |
| 255 | if mgr.websockets.contains_key(&key) { |
| 256 | return; |
| 257 | } |
| 258 | |
| 259 | let mut config = WsConfig::new(); |
| 260 | f(&mut config); |
| 261 | |
| 262 | #[cfg(not(target_arch = "wasm32"))] |
| 263 | { |
| 264 | let url = url.to_owned(); |
| 265 | let (incoming_tx, incoming_rx) = std::sync::mpsc::channel(); |
| 266 | let (outgoing_tx, mut outgoing_rx) = tokio::sync::mpsc::unbounded_channel(); |
| 267 | |
| 268 | let runtime = tokio::runtime::Builder::new_multi_thread() |
| 269 | .enable_all() |
| 270 | .build() |
| 271 | .expect("Failed to create tokio runtime for WebSocket"); |
| 272 | |
| 273 | let insecure = config.insecure; |
| 274 | let headers = config.headers; |
| 275 | |
| 276 | runtime.spawn(async move { |
| 277 | use futures::{SinkExt, StreamExt}; |
| 278 | use tokio_tungstenite::tungstenite; |
| 279 | use tungstenite::client::IntoClientRequest; |
| 280 | |
| 281 | // Build handshake request: start from URL to get proper WS headers, |
| 282 | // then add custom headers on top. |
| 283 | let mut ws_request = match url.into_client_request() { |
| 284 | Ok(r) => r, |
| 285 | Err(e) => { |
| 286 | let _ = incoming_tx.send(WsMessage::Error(e.to_string())); |
| 287 | return; |
| 288 | } |
| 289 | }; |
| 290 | for (key, value) in &headers { |
| 291 | if let (Ok(name), Ok(val)) = ( |
| 292 | tungstenite::http::header::HeaderName::from_bytes(key.as_bytes()), |
| 293 | tungstenite::http::header::HeaderValue::from_str(value), |
| 294 | ) { |
| 295 | ws_request.headers_mut().insert(name, val); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | let socket = if insecure { |
| 300 | let tls_config = { |
| 301 | let config = rustls::ClientConfig::builder() |
| 302 | .dangerous() |
| 303 | .with_custom_certificate_verifier(std::sync::Arc::new( |
| 304 | NoCertificateVerification {}, |