From aab225746f56381fad2d261c44d2860fba8c8aec Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Sat, 14 Feb 2026 16:16:33 +0100 Subject: [PATCH 1/4] [TEMP] Split Ws & Http server --- src/main.rs | 5 +- src/server/server.rs | 531 ++++++++++++++----------------------------- 2 files changed, 170 insertions(+), 366 deletions(-) diff --git a/src/main.rs b/src/main.rs index 07b461c..aa6a3f9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,7 +43,8 @@ async fn main() { } else { log!(" Users"); } - server::server::start(9187).await; - log!(" Server"); + server::server::start(9187, 9188).await; + log!(" API Server on 9187"); + log!(" WS Server on 9188"); tokio::signal::ctrl_c().await.unwrap(); } diff --git a/src/server/server.rs b/src/server/server.rs index 3926f08..22c24c6 100755 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -1,45 +1,36 @@ use base64::Engine; use base64::engine::general_purpose::STANDARD; use bytes::Bytes; -use futures::StreamExt; use futures_util::TryFutureExt; use http_body_util::BodyExt; use http_body_util::Full; -use hyper::server::conn::http2; +use hyper::server::conn::{http1, http2}; use hyper::{Method, StatusCode}; -use hyper::{ - Request as HttpRequest, Response as HttpResponse, body::Incoming, server::conn::http1, upgrade, -}; +use hyper::{Request as HttpRequest, Response as HttpResponse, body::Incoming, upgrade}; use hyper_util::rt::TokioExecutor; use hyper_util::rt::tokio::TokioIo; use hyper_util::service::TowerToHyperService; -use pnet::datalink::NetworkInterface; -use rustls::ServerConfig; -use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use sha1::{Digest, Sha1}; -use std::error::Error; -use std::io::ErrorKind; -use std::io::{self, BufReader}; -use std::net::{IpAddr, SocketAddr}; +use std::io; +use std::net::SocketAddr; use std::result::Result::Ok; -use std::sync::Arc; use std::{future::Future, pin::Pin, time::Duration}; use tokio::net::TcpListener; -use tokio::sync::broadcast; -use tokio_rustls::TlsAcceptor; use tower::Service; use crate::log; use crate::server::api; use crate::server::short_link::get_short_link; use crate::server::socket; -use crate::util::file_util::load_file_buf; + +// --- ApiService for HTTP/2 --- + #[derive(Clone)] -struct HttpService { - peer_addr: SocketAddr, +struct ApiService { + _peer_addr: SocketAddr, } -impl Service> for HttpService { +impl Service> for ApiService { type Response = HttpResponse>; type Error = io::Error; type Future = Pin> + Send>>; @@ -52,61 +43,17 @@ impl Service> for HttpService { } fn call(&mut self, req: HttpRequest) -> Self::Future { - let peer_ip = self.peer_addr.ip(); - let (parts, body) = req.into_parts(); - - let method = parts.method.clone(); let path = parts.uri.path().to_string(); let headers = parts.headers.clone(); let fut = async move { - let is_websocket_upgrade = path.starts_with("/ws") - && method == Method::GET - && headers - .get("connection") - .map(|v| v.to_str().unwrap_or("").contains("Upgrade")) - .unwrap_or(false) - && headers.get("upgrade").map(|v| v.to_str().unwrap_or("")) == Some("websocket"); - - if is_websocket_upgrade { - log!("Attempting WebSocket upgrade on /ws"); - - if let Some(sec_websocket_key) = headers.get("sec-websocket-key") { - let sec_websocket_key = sec_websocket_key.to_str().unwrap_or("").to_string(); - let sec_websocket_accept = calculate_accept_key(&sec_websocket_key); - - let response = HttpResponse::builder() - .status(StatusCode::SWITCHING_PROTOCOLS) - .header("Upgrade", "websocket") - .header("Connection", "Upgrade") - .header("Sec-WebSocket-Accept", sec_websocket_accept) - .body(Full::new(Bytes::from(""))) - .unwrap(); - let req_for_upgrade = HttpRequest::from_parts(parts, body); - let upgrades = upgrade::on(req_for_upgrade); - - socket::handle(path, upgrades); - Ok(response) - } else { - log!("No Sec-WebSocket-Key found in request headers"); - let response = HttpResponse::builder() - .status(StatusCode::BAD_REQUEST) - .body(Full::new(Bytes::from("Missing Sec-WebSocket-Key"))) - .unwrap(); - Ok(response) - } - } else if path.starts_with("/api") { + if path.starts_with("/api") { let whole_body = tokio::time::timeout(Duration::from_secs(10), body.collect()).await??; let bytes = whole_body.to_bytes(); - - let body_string: Option = match String::from_utf8(bytes.to_vec()) { - Ok(s) => Some(s), - Err(_) => None, - }; - - Ok(api::handle(&path, headers.clone(), body_string).await) + let body_string: Option = String::from_utf8(bytes.to_vec()).ok(); + Ok(api::handle(&path, headers, body_string).await) } else if path.starts_with("/direct") { let short = path.replace("/direct/", ""); if let Ok(long) = get_short_link(&short).await { @@ -124,363 +71,219 @@ impl Service> for HttpService { Ok(response) } } else { - let whole_body = match body.collect().await { - Ok(collected) => collected, - Err(e) => { - log!("Error collecting body: {}", e); - return Ok(HttpResponse::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Full::new(Bytes::from(format!( - "Failed to read body: {}", - e - )))) - .unwrap()); - } - }; - let bytes = whole_body.to_bytes(); - - let body_string: Option = match String::from_utf8(bytes.to_vec()) { - Ok(s) => Some(s), - Err(_) => None, - }; - - Ok(api::handle(&path, headers, body_string).await) + // Not a WebSocket, /api, or /direct, so it's a 404 + let response = HttpResponse::builder() + .status(StatusCode::NOT_FOUND) + .body(Full::new(Bytes::from("Not Found"))) + .unwrap(); + Ok(response) } }; Box::pin(fut.map_err(|err: color_eyre::eyre::ErrReport| { io::Error::new( io::ErrorKind::Other, - format!("Error in request handling: {}", err), + format!("Error in API request: {}", err), ) })) } } -pub fn is_local_network(addr: IpAddr) -> bool { - match addr { - IpAddr::V4(v4) => { - let octets = v4.octets(); - if octets[0] == 10 { - return true; - } - if octets[0] == 172 && (16..=31).contains(&octets[1]) { - return true; - } - if octets[0] == 192 && octets[1] == 168 { - return true; - } - if octets[0] == 127 { - return true; - } - if octets[0] == 169 && octets[1] == 254 { - return true; - } +// --- WebSocketService for HTTP/1.1 --- - false - } +#[derive(Clone)] +struct WebSocketService { + _peer_addr: SocketAddr, +} - IpAddr::V6(v6) => { - let segments = v6.segments(); - if (segments[0] & 0xfe00) == 0xfc00 { - return true; - } - if (segments[0] & 0xffc0) == 0xfe80 { - return true; - } - if v6.is_loopback() { - return true; - } +impl Service> for WebSocketService { + type Response = HttpResponse>; + type Error = io::Error; + type Future = Pin> + Send>>; - false - } + fn poll_ready( + &mut self, + _cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + std::task::Poll::Ready(std::io::Result::Ok(())) + } + + fn call(&mut self, req: HttpRequest) -> Self::Future { + let (parts, body) = req.into_parts(); + let method = parts.method.clone(); + let path = parts.uri.path().to_string(); + let headers = parts.headers.clone(); + + let fut = async move { + let is_websocket_upgrade = path.starts_with("/ws") + && method == Method::GET + && headers + .get("connection") + .map(|v| v.to_str().unwrap_or("").contains("Upgrade")) + .unwrap_or(false) + && headers.get("upgrade").map(|v| v.to_str().unwrap_or("")) == Some("websocket"); + + if is_websocket_upgrade { + log!("Attempting WebSocket upgrade on /ws"); + if let Some(sec_websocket_key) = headers.get("sec-websocket-key") { + let sec_websocket_key_str = + sec_websocket_key.to_str().unwrap_or("").to_string(); + let sec_websocket_accept = calculate_accept_key(&sec_websocket_key_str); + + let response = HttpResponse::builder() + .status(StatusCode::SWITCHING_PROTOCOLS) + .header("Upgrade", "websocket") + .header("Connection", "Upgrade") + .header("Sec-WebSocket-Accept", sec_websocket_accept) + .body(Full::new(Bytes::from(""))) + .unwrap(); + + let req_for_upgrade = HttpRequest::from_parts(parts, body); + let upgrades = upgrade::on(req_for_upgrade); + socket::handle(path, upgrades); + Ok(response) + } else { + log!("No Sec-WebSocket-Key found in request headers"); + let response = HttpResponse::builder() + .status(StatusCode::BAD_REQUEST) + .body(Full::new(Bytes::from("Missing Sec-WebSocket-Key"))) + .unwrap(); + Ok(response) + } + } else { + let response = HttpResponse::builder() + .status(StatusCode::NOT_FOUND) + .body(Full::new(Bytes::from( + "Not Found: Use the API server for non-WebSocket requests.", + ))) + .unwrap(); + Ok(response) + } + }; + + Box::pin(fut.map_err(|_err: color_eyre::eyre::ErrReport| { + io::Error::new(io::ErrorKind::Other, "Error in WebSocket request") + })) } } -async fn run_http_server(port: u16) -> bool { +async fn run_api_server(port: u16) -> bool { let mut ip = "0.0.0.0".to_string(); for iface in pnet::datalink::interfaces() { - let iface: NetworkInterface = iface; - if iface.ips.len() > 0 { - let ipsv = format!("{}", iface.ips[0]); - let ips: &str = ipsv.split('/').next().unwrap(); - if format!("{}", ips).starts_with("10.") || format!("{}", ips).starts_with("192.") { - ip = ips.to_string(); - } + if let Some(ip_net) = iface.ips.iter().find(|ip_net| { + ip_net.is_ipv4() + && (ip_net.ip().to_string().starts_with("10.") + || ip_net.ip().to_string().starts_with("192.")) + }) { + ip = ip_net.ip().to_string(); + break; } } - let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await; - if let Err(e) = listener { - log!("Failed to bind to port {}: {:?}", port, e); + + let addr = SocketAddr::new(ip.parse().unwrap(), port); + let listener_res = TcpListener::bind(format!("0.0.0.0:{}", port)).await; + if let Err(e) = listener_res { + log!( + "[FATAL] Failed to bind API server to port {}: {:?}", + port, + e + ); return false; } - let listener = listener.unwrap(); - log!( - "Standard Server listening for HTTP and WS on {}:{}", - ip, - port - ); - - // Create a broadcast channel for graceful shutdown signal - let (shutdown_tx, _) = broadcast::channel::<()>(1); + let listener = listener_res.unwrap(); + log!("API Server (HTTP/2) listening on {}:{}", ip, port); tokio::spawn(async move { loop { - tokio::select! { - // Monitor for shutdown signal - _ = async { - loop { - tokio::time::sleep(Duration::from_millis(100)).await; - } - } => { - log!("Standard Server received shutdown signal."); - // Send kill signal to all active connection tasks - let _ = shutdown_tx.send(()); - break; - } - - // Accept new connections - accepted = listener.accept() => { - match accepted { - std::result::Result::Ok((stream, addr)) => { - let service = HttpService { peer_addr: addr }; - let io = TokioIo::new(stream); - - // Subscribe to the shutdown signal for this specific connection - let mut rx = shutdown_tx.subscribe(); - - tokio::spawn(async move { - use hyper::server::conn::http2; - - let conn = http2::Builder::new(TokioExecutor::new()) - .serve_connection(io, TowerToHyperService::new(service)); - - - // Wait for either the connection to finish naturally OR the shutdown signal - tokio::select! { - res = conn => { - if let Err(err) = res { - if let Some(io_err) = err.source().and_then(|e| e.downcast_ref::()) { - if io_err.kind() != io::ErrorKind::ConnectionReset - && io_err.kind() != io::ErrorKind::BrokenPipe - { - log!("Error serving connection: {:?}", err); - } - } - } - } - _ = rx.recv() => { - // Shutdown signal received. - // Dropping the 'conn' future here closes the socket immediately. - } - } - }); - } - Err(e) => { - log!("Error accepting connection: {:?}", e); - tokio::time::sleep(Duration::from_millis(500)).await; + if let Ok((stream, addr)) = listener.accept().await { + let service = ApiService { _peer_addr: addr }; + let io = TokioIo::new(stream); + tokio::spawn(async move { + let conn = http2::Builder::new(TokioExecutor::new()) + .serve_connection(io, TowerToHyperService::new(service)); + if let Err(err) = conn.await { + if !err + .to_string() + .starts_with("error shutting down connection") + { + log!("API server connection error: {:?}", err); } } - } + }); } } - - log!("Standard Server shutdown complete."); }); true } -/// Runs the encrypted HTTPS/WSS server loop using the provided TLS config. -async fn run_tls_server(port: u16, tls_config: Arc) -> bool { +async fn run_websocket_server(port: u16) -> bool { let mut ip = "0.0.0.0".to_string(); for iface in pnet::datalink::interfaces() { - let iface: NetworkInterface = iface; - let ipsv = format!("{}", iface.ips[0]); - let ips: &str = ipsv.split('/').next().unwrap(); - log!("{}", ips); - if format!("{}", ips).starts_with("10.") { - ip = ips.to_string(); + if let Some(ip_net) = iface.ips.iter().find(|ip_net| { + ip_net.is_ipv4() + && (ip_net.ip().to_string().starts_with("10.") + || ip_net.ip().to_string().starts_with("192.")) + }) { + ip = ip_net.ip().to_string(); + break; } } - let acceptor = TlsAcceptor::from(tls_config); - - let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await; - if let Err(e) = listener { - log!("Failed to bind to port {}: {:?}", port, e); + let addr = SocketAddr::new(ip.parse().unwrap(), port); + let listener_res = TcpListener::bind(format!("0.0.0.0:{}", port)).await; + if let Err(e) = listener_res { + log!( + "[FATAL] Failed to bind WebSocket server to port {}: {:?}", + port, + e + ); return false; } - let listener = listener.unwrap(); - log!( - "Encrypted Server listening for HTTPS and WSS on {}:{}", - ip, - port - ); - - // Create a broadcast channel for graceful shutdown signal - let (shutdown_tx, _) = broadcast::channel::<()>(1); + let listener = listener_res.unwrap(); + log!("WebSocket Server (HTTP/1.1) listening on {}:{}", ip, port); tokio::spawn(async move { loop { - tokio::select! { - // Monitor for shutdown signal - _ = async { - loop { - tokio::time::sleep(Duration::from_millis(100)).await; - } - } => { - log!("Encrypted Server received shutdown signal."); - // Send kill signal to all active connection tasks - let _ = shutdown_tx.send(()); - break; - } - - // Accept new connections - accepted = listener.accept() => { - match accepted { - std::result::Result::Ok((stream, addr)) => { - let service = HttpService { peer_addr: addr }; - let acceptor = acceptor.clone(); - - // Subscribe to the shutdown signal for this specific connection - let mut rx = shutdown_tx.subscribe(); - - tokio::spawn(async move { - // Perform TLS handshake - let tls_stream = match acceptor.accept(stream).await { - Ok(s) => s, - Err(e) => { - if e.kind() != io::ErrorKind::Interrupted { - log!("TLS Handshake error: {:?}", e); - } - return; - } - }; - let io = TokioIo::new(tls_stream); - - // Prepare connection future - let conn = http1::Builder::new() - .preserve_header_case(true) - .title_case_headers(true) - .serve_connection(io, TowerToHyperService::new(service)) - .with_upgrades(); - - // Wait for either the connection to finish naturally OR the shutdown signal - tokio::select! { - res = conn => { - if let Err(err) = res { - if let Some(io_err) = err.source().and_then(|e| e.downcast_ref::()) { - if io_err.kind() != io::ErrorKind::ConnectionReset - && io_err.kind() != io::ErrorKind::BrokenPipe - { - log!("Error serving connection: {:?}", err); - } - } - } - } - _ = rx.recv() => { - // Shutdown signal received. - // Dropping the 'conn' future here closes the socket immediately. - } - } - }); - } - Err(e) => { - log!("Error accepting connection: {:?}", e); - tokio::time::sleep(Duration::from_millis(500)).await; + if let Ok((stream, addr)) = listener.accept().await { + let service = WebSocketService { _peer_addr: addr }; + let io = TokioIo::new(stream); + tokio::spawn(async move { + let conn = http1::Builder::new() + .preserve_header_case(true) + .title_case_headers(true) + .serve_connection(io, TowerToHyperService::new(service)) + .with_upgrades(); + if let Err(err) = conn.await { + if !err + .to_string() + .starts_with("error shutting down connection") + { + log!("WebSocket server connection error: {:?}", err); } } - } + }); } } - - log!("Encrypted Server shutdown complete."); }); + true } -pub async fn start(port: u16) -> bool { - let tls_result = load_tls_config(); +// --- Main Start function --- - match tls_result { - Ok(Some(tls_config)) => run_tls_server(port, tls_config).await, - Ok(_) => run_http_server(port).await, - Err(e) => { - log!("Fatal error during TLS config load: {}", e); - false - } - } +pub async fn start(api_port: u16, ws_port: u16) { + // We are not using TLS for this setup as per the request's focus on splitting protocols. + // The previous TLS loading logic is removed for simplicity. + tokio::spawn(run_api_server(api_port)); + tokio::spawn(run_websocket_server(ws_port)); } + fn calculate_accept_key(key: &str) -> String { let websocket_guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; let mut sha1 = Sha1::new(); sha1.update(key.as_bytes()); sha1.update(websocket_guid.as_bytes()); let result = sha1.finalize(); - STANDARD.encode(result) // Base64 encode the result -} - -/// Loads TLS config. Returns Ok(None) if cert files are not found, and an error if parsing fails. -fn load_tls_config() -> Result>, Box> { - let cert_file_res = load_file_buf("certs", "cert.pem"); - let key_file_res = load_file_buf("certs", "cert.key"); - - // Check if certificate files are present. If not, return None. - let cert_file_buf = match cert_file_res { - Ok(b) => b, - Err(e) if e.kind() == ErrorKind::NotFound => { - log!("TLS certificate 'certs/cert.pem' not found."); - return Ok(None); - } - Err(e) => return Err(e.into()), // Other IO error - }; - - let key_file_buf = match key_file_res { - Ok(b) => b, - Err(e) if e.kind() == ErrorKind::NotFound => { - log!("TLS key 'certs/cert.key' not found."); - return Ok(None); - } - Err(e) => return Err(e.into()), // Other IO error - }; - - // Continue with configuration if both files were found - let mut cert_reader = BufReader::new(cert_file_buf); - let cert_ders = rustls_pemfile::certs(&mut cert_reader) - .collect::, io::Error>>()?; - - // PKCS8 - let mut key_reader = BufReader::new(key_file_buf); - let mut key_ders = rustls_pemfile::pkcs8_private_keys(&mut key_reader) - .map(|r| r.map(Into::into)) // Explicit conversion - .collect::, io::Error>>()?; - - if key_ders.is_empty() { - // RSA - key_reader = BufReader::new(load_file_buf("certs", "cert.key")?); // Re-read key file - key_ders = rustls_pemfile::rsa_private_keys(&mut key_reader) - .map(|r| r.map(Into::into)) - .collect::, io::Error>>()?; - } - - if key_ders.is_empty() { - // EC - key_reader = BufReader::new(load_file_buf("certs", "cert.key")?); // Re-read key file - key_ders = rustls_pemfile::ec_private_keys(&mut key_reader) - .map(|r| r.map(Into::into)) - .collect::, io::Error>>()?; - } - - if key_ders.is_empty() { - return Err("No private keys found in key file. (Tried PKCS8, RSA, and EC)".into()); - } - - let mut config = rustls::ServerConfig::builder() - .with_no_client_auth() - .with_single_cert(cert_ders, key_ders.remove(0))?; - - config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - - Ok(Some(Arc::new(config))) + STANDARD.encode(result) } From cb7579d86dcb88ca6fc6408ae91ad6cad9503b5c Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Sun, 15 Feb 2026 00:37:42 +0100 Subject: [PATCH 2/4] [FIX] Websocket & webserver is now actix, because that should be better than hyper & axum, i guess we'll see in the long run :}, ToDo: IF THIS WORKS: implement on the iota --- Cargo.lock | 1280 ++++++++++++++++++++++++------ Cargo.toml | 16 +- src/main.rs | 5 +- src/server/api.rs | 447 +++++------ src/server/omikron_connection.rs | 39 +- src/server/server.rs | 438 ++-------- src/server/socket.rs | 186 +++-- 7 files changed, 1441 insertions(+), 970 deletions(-) mode change 100644 => 100755 src/server/socket.rs diff --git a/Cargo.lock b/Cargo.lock index ba72ade..7469a5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,8 +6,13 @@ version = 4 name = "Omega" version = "0.1.0" dependencies = [ + "actix", + "actix-rt", + "actix-web", + "actix-web-actors", "aes-gcm", "ansi_term", + "anyhow", "async-trait", "async-tungstenite", "axum", @@ -22,6 +27,7 @@ dependencies = [ "hkdf", "http-body-util", "hyper", + "hyper-rustls", "hyper-util", "json", "once_cell", @@ -49,6 +55,265 @@ dependencies = [ "zip", ] +[[package]] +name = "actix" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" +dependencies = [ + "actix-macros", + "actix-rt", + "actix_derive", + "bitflags 2.11.0", + "bytes", + "crossbeam-channel", + "futures-core", + "futures-sink", + "futures-task", + "futures-util", + "log", + "once_cell", + "parking_lot", + "pin-project-lite", + "smallvec", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags 2.11.0", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7926860314cbe2fb5d1f13731e387ab43bd32bca224e82e6e2db85de0a3dba49" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-tls", + "actix-utils", + "base64 0.22.1", + "bitflags 2.11.0", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "foldhash", + "futures-core", + "h2 0.3.27", + "http 0.2.12", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand 0.9.2", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http 0.2.12", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92589714878ca59a7626ea19734f0e07a6a875197eec751bb5d3f99e64998c63" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "actix-tls" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6176099de3f58fbddac916a7f8c6db297e021d706e7a6b99947785fee14abe9f" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "impl-more", + "pin-project-lite", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1654a77ba142e37f049637a3e5685f864514af11fcbc51cb51eb6596afe5b8d6" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-tls", + "actix-utils", + "actix-web-codegen", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "foldhash", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.6.2", + "time", + "tracing", + "url", +] + +[[package]] +name = "actix-web-actors" +version = "4.3.1+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98c5300b38fd004fe7d2a964f9a90813fdbe8a81fed500587e78b1b71c6f980" +dependencies = [ + "actix", + "actix-codec", + "actix-http", + "actix-web", + "bytes", + "bytestring", + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix_derive" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -108,6 +373,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -123,6 +403,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + [[package]] name = "arbitrary" version = "1.4.2" @@ -337,7 +623,7 @@ dependencies = [ "tokio-openssl", "tokio-rustls", "tungstenite", - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] @@ -363,9 +649,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.15.2" +version = "1.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88aab2464f1f25453baa7a07c84c5b7684e274054ba06817f382357f77a288" +checksum = "7b7b6141e96a8c160799cc2d5adecd5cbbe5054cb8c7c4af53da0f83bb7ad256" dependencies = [ "aws-lc-sys", "zeroize", @@ -373,9 +659,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.35.0" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45afffdee1e7c9126814751f88dddc747f41d91da16c9551a0f1e8a11e788a1" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" dependencies = [ "cc", "cmake", @@ -394,7 +680,7 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http", + "http 1.4.0", "http-body", "http-body-util", "hyper", @@ -427,7 +713,7 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http", + "http 1.4.0", "http-body", "http-body-util", "mime", @@ -467,9 +753,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bitflags" @@ -479,9 +765,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ "serde_core", ] @@ -508,6 +794,27 @@ dependencies = [ "piper", ] +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bumpalo" version = "3.19.1" @@ -526,6 +833,15 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +[[package]] +name = "bytestring" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "113b4343b5f6617e7ad401ced8de3cc8b012e73a594347c307b90db3e9271289" +dependencies = [ + "bytes", +] + [[package]] name = "bzip2" version = "0.6.1" @@ -537,9 +853,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.51" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "jobserver", @@ -548,10 +864,16 @@ dependencies = [ ] [[package]] -name = "cfg-expr" -version = "0.20.5" +name = "cesu8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21be0e1ce6cdb2ee7fff840f922fb04ead349e5cfb1e750b769132d44ce04720" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-expr" +version = "0.20.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78cef5b5a1a6827c7322ae2a636368a573006b27cfa76c7ebd53e834daeaab6a" dependencies = [ "smallvec", "target-lexicon", @@ -563,6 +885,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cipher" version = "0.4.4" @@ -609,6 +937,16 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -630,6 +968,26 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -689,6 +1047,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-queue" version = "0.3.12" @@ -740,9 +1107,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "deflate64" @@ -763,9 +1130,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "cc3dc5ad92c2e2d1c193bbbbdf2ea477cb81331de4f3103f267ca18368b988c4" dependencies = [ "powerfmt", ] @@ -781,6 +1148,29 @@ dependencies = [ "syn", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -938,19 +1328,19 @@ checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" [[package]] name = "find-msvc-tools" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", - "libz-rs-sys", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -1146,13 +1536,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -1162,9 +1554,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", ] [[package]] @@ -1219,7 +1626,7 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "futures-channel", "futures-core", "futures-executor", @@ -1286,6 +1693,25 @@ dependencies = [ "system-deps", ] +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "h2" version = "0.4.13" @@ -1297,7 +1723,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -1388,6 +1814,17 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.4.0" @@ -1405,7 +1842,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.4.0", ] [[package]] @@ -1416,7 +1853,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http", + "http 1.4.0", "http-body", "pin-project-lite", ] @@ -1443,8 +1880,8 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", - "http", + "h2 0.4.13", + "http 1.4.0", "http-body", "httparse", "httpdate", @@ -1462,32 +1899,18 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http", + "http 1.4.0", "hyper", "hyper-util", + "log", "rustls 0.23.36", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", ] -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - [[package]] name = "hyper-util" version = "0.1.20" @@ -1498,16 +1921,17 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", + "http 1.4.0", "http-body", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.2", "system-configuration", "tokio", + "tower-layer", "tower-service", "tracing", "windows-registry", @@ -1594,6 +2018,12 @@ dependencies = [ "zerovec", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "idna" version = "1.1.0" @@ -1615,6 +2045,12 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + [[package]] name = "indenter" version = "0.3.4" @@ -1623,12 +2059,14 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", + "serde", + "serde_core", ] [[package]] @@ -1691,6 +2129,28 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.34" @@ -1703,9 +2163,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" dependencies = [ "once_cell", "wasm-bindgen", @@ -1726,6 +2186,12 @@ dependencies = [ "log", ] +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.5.0" @@ -1735,6 +2201,12 @@ dependencies = [ "spin", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libbz2-rs-sys" version = "0.2.2" @@ -1743,15 +2215,15 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.179" +version = "0.2.182" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" @@ -1759,9 +2231,9 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", - "redox_syscall 0.7.0", + "redox_syscall 0.7.1", ] [[package]] @@ -1774,15 +2246,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "libz-rs-sys" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415" -dependencies = [ - "zlib-rs", -] - [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -1801,6 +2264,23 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + [[package]] name = "lock_api" version = "0.4.14" @@ -1819,6 +2299,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lzma-rust2" version = "0.13.0" @@ -1847,9 +2333,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "mime" @@ -1874,23 +2360,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.61.2", ] [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "9d5d26952a508f321b4d3d2e80e78fc2603eaefcdf0c30783867f19586518bdc" dependencies = [ "libc", "log", "openssl", - "openssl-probe 0.1.6", + "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.1", + "security-framework", "security-framework-sys", "tempfile", ] @@ -1903,9 +2390,9 @@ checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" [[package]] name = "ntapi" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -1928,9 +2415,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-integer" @@ -1968,7 +2455,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -2008,7 +2495,7 @@ version = "0.10.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", @@ -2030,15 +2517,9 @@ dependencies = [ [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-probe" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f50d9b3dabb09ecd771ad0aa242ca6894994c130308ca3d7684634df8037391" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" @@ -2312,9 +2793,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppmd-rust" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d558c559f0450f16f2a27a1f017ef38468c1090c9ce63c8e51366232d53717b4" +checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" [[package]] name = "ppv-lite86" @@ -2325,6 +2806,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro-crate" version = "3.4.0" @@ -2336,18 +2827,74 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] -name = "quote" -version = "1.0.43" +name = "quinn" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.36", + "socket2 0.6.2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.2", + "ring", + "rustc-hash", + "rustls 0.23.36", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ "proc-macro2", ] @@ -2376,7 +2923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2396,7 +2943,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -2411,14 +2958,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", ] @@ -2429,23 +2976,23 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -2455,9 +3002,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -2465,42 +3012,46 @@ dependencies = [ ] [[package]] -name = "regex-syntax" -version = "0.8.8" +name = "regex-lite" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", "futures-core", - "h2", - "http", + "h2 0.4.13", + "http 1.4.0", "http-body", "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", "mime", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls 0.23.36", "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", + "rustls-platform-verifier", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -2518,7 +3069,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.16", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", @@ -2546,9 +3097,24 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -2570,7 +3136,7 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.11.0", @@ -2599,7 +3165,7 @@ dependencies = [ "log", "once_cell", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.9", "subtle", "zeroize", ] @@ -2610,10 +3176,10 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe 0.2.0", + "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework", ] [[package]] @@ -2636,13 +3202,41 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.2" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ + "web-time", "zeroize", ] +[[package]] +name = "rustls-platform-verifier" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +dependencies = [ + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.36", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.103.9", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -2655,9 +3249,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ "aws-lc-rs", "ring", @@ -2673,9 +3267,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -2713,24 +3307,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" -dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -2739,14 +3320,20 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -2887,9 +3474,9 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -2912,9 +3499,19 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ "libc", "windows-sys 0.60.2", @@ -2981,7 +3578,7 @@ dependencies = [ "serde_json", "sha2", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "url", ] @@ -3032,7 +3629,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.0", "byteorder", "bytes", "crc", @@ -3061,7 +3658,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "whoami", ] @@ -3074,7 +3671,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.0", "byteorder", "crc", "dotenvy", @@ -3098,7 +3695,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "whoami", ] @@ -3122,7 +3719,7 @@ dependencies = [ "serde", "serde_urlencoded", "sqlx-core", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "url", ] @@ -3170,9 +3767,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.114" +version = "2.0.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" dependencies = [ "proc-macro2", "quote", @@ -3219,7 +3816,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -3255,12 +3852,12 @@ checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" [[package]] name = "tempfile" -version = "3.24.0" +version = "3.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" dependencies = [ "fastrand 2.3.0", - "getrandom 0.3.4", + "getrandom 0.4.1", "once_cell", "rustix 1.1.3", "windows-sys 0.61.2", @@ -3277,11 +3874,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -3297,9 +3894,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -3317,22 +3914,34 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", + "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] [[package]] name = "tinystr" @@ -3371,7 +3980,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2 0.6.2", "tokio-macros", "windows-sys 0.61.2", ] @@ -3449,9 +4058,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.10+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", @@ -3485,9 +4094,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.0.8+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "0742ff5ff03ea7e67c8ae6c93cac239e0d9784833362da3f9a9c1da8dfefcbdc" dependencies = [ "winnow", ] @@ -3500,9 +4109,9 @@ checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -3520,10 +4129,10 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-util", - "http", + "http 1.4.0", "http-body", "iri-string", "pin-project-lite", @@ -3612,7 +4221,7 @@ checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", - "http", + "http 1.4.0", "httparse", "log", "native-tls", @@ -3620,7 +4229,7 @@ dependencies = [ "rustls 0.23.36", "rustls-pki-types", "sha1", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "utf-8", ] @@ -3639,9 +4248,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" [[package]] name = "unicode-normalization" @@ -3658,6 +4267,18 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" @@ -3700,11 +4321,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" +checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.1", "js-sys", "wasm-bindgen", ] @@ -3772,9 +4393,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] @@ -3787,9 +4417,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" dependencies = [ "cfg-if", "once_cell", @@ -3800,11 +4430,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.56" +version = "0.4.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" dependencies = [ "cfg-if", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -3813,9 +4444,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3823,9 +4454,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" dependencies = [ "bumpalo", "proc-macro2", @@ -3836,18 +4467,62 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" dependencies = [ "unicode-ident", ] [[package]] -name = "web-sys" -version = "0.3.83" +name = "wasm-encoder" +version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -3863,6 +4538,15 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webpki-roots" version = "0.22.6" @@ -3874,9 +4558,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -4059,6 +4743,15 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4095,6 +4788,21 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -4152,6 +4860,12 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4170,6 +4884,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4188,6 +4908,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4218,6 +4944,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4236,6 +4968,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4254,6 +4992,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4272,6 +5016,12 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4301,9 +5051,91 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "writeable" @@ -4347,18 +5179,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.32" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fabae64378cb18147bb18bca364e63bdbe72a0ffe4adf0addfec8aa166b2c56" +checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.32" +version = "0.8.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9c2d862265a8bb4471d87e033e730f536e2a285cc7cb05dbce09a2a97075f90" +checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" dependencies = [ "proc-macro2", "quote", @@ -4468,15 +5300,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.5.5" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3" +checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c" [[package]] name = "zmij" -version = "1.0.12" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index f89a8a4..d982a18 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,13 @@ version = "0.1.0" edition = "2024" [dependencies] +actix = "0.13.5" +actix-rt = "2.11.0" +actix-web = { version = "4.12.1", features = ["rustls-0_23"] } +actix-web-actors = "4.3.1" aes-gcm = "*" ansi_term = "0.12.1" +anyhow = "1.0.101" async-trait = "0.1.89" async-tungstenite = { version = "0.32.0", features = [ "futures-03-sink", @@ -39,7 +44,7 @@ async-tungstenite = { version = "0.32.0", features = [ "verbose-logging", "webpki-roots", ] } -axum = { version = "0.8.8", features = [ "ws" ] } +axum = { version = "0.8.8", features = ["ws"] } base64 = "0.22.1" bytes = "1.11.1" color-eyre = "0.6.5" @@ -50,15 +55,16 @@ futures-util = "0.3.31" hex = "0.4.3" hkdf = "0.12.4" http-body-util = "0.1.3" -hyper = { version = "1.8.1", features = ["full"] } -hyper-util = "0.1.20" +hyper = { version = "1.8.1", features = ["http2", "full"] } +hyper-rustls = { version = "0.27.7", features = ["http2"] } +hyper-util = { version = "0.1.20", features = ["full"] } json = "0.12.4" once_cell = "1.21.3" pnet = "0.35.0" rand = "0.8" rand_core = { version = "0.6", features = ["getrandom", "std"] } -reqwest = "0.12.28" -rustls = "0.23.35" +reqwest = "0.13.2" +rustls = "0.23.36" rustls-pemfile = "2.2.0" sha1 = "0.10.6" sha2 = "0.10.9" diff --git a/src/main.rs b/src/main.rs index aa6a3f9..88637e6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,8 +43,7 @@ async fn main() { } else { log!(" Users"); } - server::server::start(9187, 9188).await; - log!(" API Server on 9187"); - log!(" WS Server on 9188"); + let _ = server::server::start(9187).await; + tokio::signal::ctrl_c().await.unwrap(); } diff --git a/src/server/api.rs b/src/server/api.rs index ae30a24..bc240dc 100644 --- a/src/server/api.rs +++ b/src/server/api.rs @@ -1,27 +1,21 @@ use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; -use crate::get_public_key; use crate::server::omikron_manager::get_random_omikron; use crate::sql::sql; use crate::sql::user_online_tracker::get_iota_primary_omikron_connection; -use crate::util::file_util::load_file_vec; +use crate::util::file_util::load_file; +use crate::{get_public_key, log}; use crate::{ sql::sql::{get_by_user_id, get_omikron_by_id}, util::crypto_helper::public_key_to_base64, }; -use axum::http::HeaderValue; +use actix_web::HttpResponse; +use actix_web::body::BoxBody; +use actix_web::http::StatusCode; use base64::Engine as _; -use http_body_util::Full; -use hyper::body::Bytes; -use hyper::header::{CONTENT_DISPOSITION, CONTENT_LENGTH, CONTENT_TYPE}; -use hyper::{HeaderMap, Response as HttpResponse, StatusCode}; use json::JsonValue; use json::number::Number; -pub async fn handle( - path: &str, - _headers: HeaderMap, - body_string: Option, -) -> HttpResponse> { +pub async fn handle(path: &str, body_string: Option) -> HttpResponse { let path_parts: Vec<&str> = path.split("/").filter(|s| !s.is_empty()).collect(); let _body: Option = if body_string.is_some() { @@ -33,258 +27,189 @@ pub async fn handle( } else { None }; - let (status, body_text) = if path_parts.len() >= 2 { - match path_parts[1] { - "download" => { - if path_parts.len() == 3 && path_parts[2] == "iota_frontend" { - let file: Bytes = load_file_vec("downloads", "iota_frontend.zip") - .unwrap() - .into(); - let len = file.len(); - let body = Full::new(file); - - let response = HttpResponse::builder() - .status(StatusCode::OK) - .header(CONTENT_TYPE, "application/zip") - .header( - CONTENT_DISPOSITION, - "attachment; filename=\"iota_frontend.zip\"", - ) - .header(CONTENT_LENGTH, len) - .body(body) - .unwrap(); - return response; - } else { - bad_request() - } - } - "get" => match path_parts[2] { - // api/get/omikron -> any omikron - // api/get/omikron/ -> omikron for id (user / iota / omikron) - "omikron" => { - if path_parts.len() == 3 { - if let Ok(omikron_conn) = get_random_omikron().await { - if let Ok((public_key, ip_address)) = - sql::get_omikron_by_id(omikron_conn.get_omikron_id().await).await - { - ( - StatusCode::OK, - format!( - "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", - omikron_conn.get_omikron_id().await, - public_key, - ip_address - ), - ) - } else { - ( - StatusCode::INTERNAL_SERVER_ERROR, - "selected an invalid omikron".to_string(), - ) - } - } else { - ( - StatusCode::NOT_FOUND, - "couldn't find online omikron".to_string(), - ) - } - } else if path_parts.len() == 4 { - let id = path_parts[3].parse::().unwrap_or(0); - if id == 0 { - not_found() - } else if let Ok((public_key, ip_address)) = get_omikron_by_id(id).await { - ( - StatusCode::OK, - format!( - "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", - id, public_key, ip_address - ), - ) - } else if let Some(omikron_id) = get_iota_primary_omikron_connection(id) { - if let Ok((public_key, ip_address)) = - get_omikron_by_id(omikron_id).await - { - ( - StatusCode::OK, - format!( - "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", - omikron_id, public_key, ip_address - ), - ) - } else { - not_found() - } - } else if let Ok((_, iota_id, _, _, _, _, _, _, _, _, _, _)) = - get_by_user_id(id).await - { - if let Some(omikron_id) = get_iota_primary_omikron_connection(iota_id) { - if let Ok((public_key, ip_address)) = - get_omikron_by_id(omikron_id).await - { - ( - StatusCode::OK, - format!( - "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", - omikron_id, public_key, ip_address - ), - ) - } else { - not_found() - } - } else { - not_found() - } - } else { - not_found() - } - } else { - bad_request() - } - } - // get/id/ - "id" => { - if path_parts.len() != 4 { - bad_request() - } else { - let username = path_parts[3]; - if username.is_empty() { - not_found() - } else { - if let Ok(( - id, - iota_id, - username, - _, - _, - _, - _, - sub_level, - sub_end, - public_key, - _, - _, - )) = sql::get_by_username(username).await - { - let cv = CommunicationValue::new(CommunicationType::success) - .add_data_str(DataTypes::username, username) - .add_data_str(DataTypes::public_key, public_key) - .add_data( - DataTypes::user_id, - JsonValue::Number(Number::from(id)), - ) - .add_data( - DataTypes::iota_id, - JsonValue::Number(Number::from(iota_id)), - ) - .add_data( - DataTypes::sub_level, - JsonValue::Number(Number::from(sub_level)), - ) - .add_data( - DataTypes::sub_end, - JsonValue::Number(Number::from(sub_end)), - ); - (StatusCode::OK, cv.to_json().to_string()) - } else { - ( - StatusCode::OK, - CommunicationValue::new(CommunicationType::error_not_found) - .to_json() - .to_string(), - ) - } - } - } - } - "public_key" => (StatusCode::OK, public_key_to_base64(&get_public_key())), - "user" => { - if path_parts.len() != 4 { - bad_request() - } else { - let id = path_parts[3]; - let id: i64 = id.parse().unwrap_or(0); - if id == 0 { - bad_request() - } else { - if let Ok(( - id, - iota_id, - username, - display, - status, - about, - avatar, - sub_level, - sub_end, - public_key, - _, - _, - )) = sql::get_by_user_id(id).await - { - let mut cv = CommunicationValue::new(CommunicationType::success) - .add_data_str(DataTypes::username, username) - .add_data_str(DataTypes::public_key, public_key) - .add_data( - DataTypes::user_id, - JsonValue::Number(Number::from(id)), - ) - .add_data( - DataTypes::iota_id, - JsonValue::Number(Number::from(iota_id)), - ) - .add_data( - DataTypes::sub_level, - JsonValue::Number(Number::from(sub_level)), - ) - .add_data( - DataTypes::sub_end, - JsonValue::Number(Number::from(sub_end)), - ); - if let Some(display) = display { - cv = cv.add_data_str(DataTypes::display, display); - } - if let Some(status) = status { - cv = cv.add_data_str(DataTypes::status, status); - } - if let Some(about) = about { - cv = cv.add_data_str(DataTypes::about, about); - } - if let Some(avatar) = avatar { - cv = cv.add_data_str( - DataTypes::avatar, - base64::engine::general_purpose::STANDARD.encode(avatar), - ); - } - (StatusCode::OK, cv.to_json().to_string()) - } else { - ( - StatusCode::OK, - CommunicationValue::new(CommunicationType::error_not_found) - .to_json() - .to_string(), - ) - } - } - } - } - _ => { - let id = path_parts[2]; - let id: i64 = id.parse().unwrap_or(0); - if id == 0 { - bad_request() - } else { - bad_request() - } - } - }, - _ => not_found(), + log!("Path parts: {:?}", path_parts); + let (status, body_text) = match path_parts.as_slice() { + ["api", "download", "iota_frontend"] => { + let file = load_file("downloads", "iota_frontend.zip"); + (StatusCode::OK, file) } - } else { - not_found() + ["api", "get", "omikron"] => { + if let Ok(omikron_conn) = get_random_omikron().await { + if let Ok((public_key, ip_address)) = + sql::get_omikron_by_id(omikron_conn.get_omikron_id().await).await + { + ( + StatusCode::OK, + format!( + "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", + omikron_conn.get_omikron_id().await, + public_key, + ip_address + ), + ) + } else { + ( + StatusCode::INTERNAL_SERVER_ERROR, + "selected an invalid omikron".to_string(), + ) + } + } else { + ( + StatusCode::NOT_FOUND, + "couldn't find online omikron".to_string(), + ) + } + } + ["api", "get", "omikron", id] => { + let id = id.parse::().unwrap_or(0); + if id == 0 { + not_found() + } else if let Ok((public_key, ip_address)) = get_omikron_by_id(id).await { + ( + StatusCode::OK, + format!( + "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", + id, public_key, ip_address + ), + ) + } else if let Some(omikron_id) = get_iota_primary_omikron_connection(id) { + if let Ok((public_key, ip_address)) = get_omikron_by_id(omikron_id).await { + ( + StatusCode::OK, + format!( + "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", + omikron_id, public_key, ip_address + ), + ) + } else { + not_found() + } + } else if let Ok((_, iota_id, _, _, _, _, _, _, _, _, _, _)) = get_by_user_id(id).await + { + if let Some(omikron_id) = get_iota_primary_omikron_connection(iota_id) { + if let Ok((public_key, ip_address)) = get_omikron_by_id(omikron_id).await { + ( + StatusCode::OK, + format!( + "{{\"id\": {}, \"public_key\": \"{}\", \"ip_address\": \"{}\"}}", + omikron_id, public_key, ip_address + ), + ) + } else { + not_found() + } + } else { + not_found() + } + } else { + not_found() + } + } + ["api", "get", "id", username] => { + if username.is_empty() { + not_found() + } else if let Ok(( + id, + iota_id, + username, + _, + _, + _, + _, + sub_level, + sub_end, + public_key, + _, + _, + )) = sql::get_by_username(username).await + { + let cv = CommunicationValue::new(CommunicationType::success) + .add_data_str(DataTypes::username, username) + .add_data_str(DataTypes::public_key, public_key) + .add_data(DataTypes::user_id, JsonValue::Number(Number::from(id))) + .add_data(DataTypes::iota_id, JsonValue::Number(Number::from(iota_id))) + .add_data( + DataTypes::sub_level, + JsonValue::Number(Number::from(sub_level)), + ) + .add_data(DataTypes::sub_end, JsonValue::Number(Number::from(sub_end))); + (StatusCode::OK, cv.to_json().to_string()) + } else { + ( + StatusCode::OK, + CommunicationValue::new(CommunicationType::error_not_found) + .to_json() + .to_string(), + ) + } + } + ["api", "get", "public_key"] => (StatusCode::OK, public_key_to_base64(&get_public_key())), + ["api", "get", "user", id] => { + let id: i64 = id.parse().unwrap_or(0); + if id == 0 { + bad_request() + } else if let Ok(( + id, + iota_id, + username, + display, + status, + about, + avatar, + sub_level, + sub_end, + public_key, + _, + _, + )) = sql::get_by_user_id(id).await + { + let mut cv = CommunicationValue::new(CommunicationType::success) + .add_data_str(DataTypes::username, username) + .add_data_str(DataTypes::public_key, public_key) + .add_data(DataTypes::user_id, JsonValue::Number(Number::from(id))) + .add_data(DataTypes::iota_id, JsonValue::Number(Number::from(iota_id))) + .add_data( + DataTypes::sub_level, + JsonValue::Number(Number::from(sub_level)), + ) + .add_data(DataTypes::sub_end, JsonValue::Number(Number::from(sub_end))); + if let Some(display) = display { + cv = cv.add_data_str(DataTypes::display, display); + } + if let Some(status) = status { + cv = cv.add_data_str(DataTypes::status, status); + } + if let Some(about) = about { + cv = cv.add_data_str(DataTypes::about, about); + } + if let Some(avatar) = avatar { + cv = cv.add_data_str( + DataTypes::avatar, + base64::engine::general_purpose::STANDARD.encode(avatar), + ); + } + (StatusCode::OK, cv.to_json().to_string()) + } else { + ( + StatusCode::OK, + CommunicationValue::new(CommunicationType::error_not_found) + .to_json() + .to_string(), + ) + } + } + _ => ( + StatusCode::INTERNAL_SERVER_ERROR, + CommunicationValue::new(CommunicationType::error) + .to_json() + .to_string(), + ), }; - let body = Full::new(Bytes::from(body_text.to_string())); - HttpResponse::builder().status(status).body(body).unwrap() + let body_bytes = body_text.to_string().into_bytes(); + let body = BoxBody::new(body_bytes.clone()); + HttpResponse::new(status).set_body(body) } + pub fn bad_request() -> (StatusCode, String) { (StatusCode::BAD_REQUEST, "400 Bad Request".to_string()) } diff --git a/src/server/omikron_connection.rs b/src/server/omikron_connection.rs index 3eeb22b..b44d1b4 100644 --- a/src/server/omikron_connection.rs +++ b/src/server/omikron_connection.rs @@ -1,19 +1,16 @@ use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::server::omikron_manager; use crate::server::short_link::add_short_link; +use crate::server::socket::{WsSendMessage, WsSession}; use crate::sql::connection_status::UserStatus; use crate::sql::sql::{self, get_by_user_id, get_by_username, get_iota_by_id, get_omikron_by_id}; use crate::sql::user_online_tracker::{self}; use crate::util::crypto_helper::encrypt; use crate::util::logger::PrintType; -use crate::{get_private_key, get_public_key, log_err, log_in, log_out}; -use axum::extract::ws::WebSocket; -use axum::extract::ws::{Message, Utf8Bytes}; +use crate::{get_private_key, get_public_key, log_in, log_out}; +use actix::Addr; use base64::{Engine as _, engine::general_purpose::STANDARD}; use dashmap::DashMap; -use futures::stream::SplitSink; -use futures::stream::SplitStream; -use futures_util::SinkExt; use json::JsonValue; use json::number::Number; use rand::Rng; @@ -24,8 +21,7 @@ use uuid::Uuid; use x448::PublicKey; pub struct OmikronConnection { - pub sender: Arc>>, - pub receiver: Arc>>, + pub ws_addr: Arc>>, pub omikron_id: Arc>, pub pub_key: Arc>>>, identified: Arc>, @@ -39,13 +35,9 @@ pub struct OmikronConnection { } impl OmikronConnection { - pub fn new( - sender: SplitSink, - receiver: SplitStream, - ) -> Arc { + pub fn new(ws_addr: Addr) -> Arc { Arc::new(Self { - sender: Arc::new(RwLock::new(sender)), - receiver: Arc::new(RwLock::new(receiver)), + ws_addr: Arc::new(RwLock::new(ws_addr)), omikron_id: Arc::new(RwLock::new(0)), pub_key: Arc::new(RwLock::new(None)), identified: Arc::new(RwLock::new(false)), @@ -56,25 +48,20 @@ impl OmikronConnection { }) } pub async fn send_message(&self, cv: &CommunicationValue) { - let mut sender = self.sender.write().await; - let message_text = Message::Text(Utf8Bytes::from(cv.to_json().to_string())); + let text = cv.to_json().to_string(); + if !cv.is_type(CommunicationType::pong) { log_out!( *self.omikron_id.read().await, PrintType::Omikron, "{}", - cv.to_json().to_string() - ); - } - if let Err(e) = sender.send(message_text).await { - log_err!( - *self.omikron_id.read().await, - PrintType::Omikron, - "WebSocket send error: {}", - e + text ); } + + self.ws_addr.read().await.do_send(WsSendMessage(text)); } + pub async fn get_omikron_id(&self) -> i64 { *self.omikron_id.read().await } @@ -948,7 +935,6 @@ impl OmikronConnection { self.send_message(&error).await; } pub async fn close(&self) { - let mut sender = self.sender.write().await; if self.is_identified().await { let omikron_id = self.get_omikron_id().await; if omikron_id != 0 { @@ -957,7 +943,6 @@ impl OmikronConnection { user_online_tracker::untrack_omikron(omikron_id).await; } } - let _ = sender.close().await; } pub async fn handle_close(self: Arc) { if self.is_identified().await { diff --git a/src/server/server.rs b/src/server/server.rs index afa0244..5167444 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -1,389 +1,83 @@ -<<<<<<< HEAD -use base64::Engine; -use base64::engine::general_purpose::STANDARD; -use bytes::Bytes; -use futures_util::TryFutureExt; -use http_body_util::BodyExt; -use http_body_util::Full; -use hyper::server::conn::{http1, http2}; -use hyper::{Method, StatusCode}; -use hyper::{Request as HttpRequest, Response as HttpResponse, body::Incoming, upgrade}; -use hyper_util::rt::TokioExecutor; -use hyper_util::rt::tokio::TokioIo; -use hyper_util::service::TowerToHyperService; -use sha1::{Digest, Sha1}; -use std::io; -use std::net::SocketAddr; -use std::result::Result::Ok; -use std::{future::Future, pin::Pin, time::Duration}; -use tokio::net::TcpListener; -use tower::Service; -======= -use axum::{ - Router, - body::Body, - extract::{ConnectInfo, OriginalUri, Path, ws::WebSocketUpgrade}, - response::{IntoResponse, Redirect}, - routing::get, +use crate::{ + log, + server::{api, short_link::get_short_link, socket}, + util::file_util::get_directory, }; -use pnet::datalink::NetworkInterface; -use std::net::SocketAddr; -use std::time::Duration; -use tokio::net::TcpListener; ->>>>>>> 7f78c8669b36cbe39755d69cccd6971e56e10290 +use actix_web::{App, HttpRequest, HttpResponse, HttpServer, Responder, http::header, web}; +use actix_web_actors::ws; -use crate::log; -use crate::server::api; -use crate::server::short_link::get_short_link; -use crate::server::socket; +use rustls::ServerConfig; +use rustls::pki_types::{CertificateDer, PrivateKeyDer}; +use rustls_pemfile::{certs, pkcs8_private_keys}; -<<<<<<< HEAD -// --- ApiService for HTTP/2 --- +use std::fs::File; +use std::io::BufReader; -#[derive(Clone)] -struct ApiService { - _peer_addr: SocketAddr, -} +pub async fn start(port: u16) -> anyhow::Result<()> { + let mut cert_reader = + BufReader::new(File::open(format!("{}/certs/cert.pem", get_directory()))?); -impl Service> for ApiService { - type Response = HttpResponse>; - type Error = io::Error; - type Future = Pin> + Send>>; -======= -pub async fn start(port: u16) -> bool { - let app = Router::new() - .route("/ws/omikron", get(ws_handler)) - .route("/direct/{short}", get(direct_handler)) - .fallback(fallback_handler); - run_http_server(port, app).await -} + let mut key_reader = BufReader::new(File::open(format!("{}/certs/key.pem", get_directory()))?); -async fn ws_handler( - ws: WebSocketUpgrade, - OriginalUri(uri): OriginalUri, - ConnectInfo(_): ConnectInfo, -) -> impl IntoResponse { - log!("Attempting WebSocket upgrade on {}", uri.path()); - let path = uri.path().to_string(); ->>>>>>> 7f78c8669b36cbe39755d69cccd6971e56e10290 + let cert_chain: Vec> = + certs(&mut cert_reader).collect::>()?; - ws.on_upgrade(async move |socket| socket::handle(path, socket)) -} + let mut keys: Vec> = pkcs8_private_keys(&mut key_reader) + .map(|res| res.map(Into::into)) + .collect::>()?; -<<<<<<< HEAD - fn call(&mut self, req: HttpRequest) -> Self::Future { - let (parts, body) = req.into_parts(); - let path = parts.uri.path().to_string(); - let headers = parts.headers.clone(); + let key = keys.remove(0); - let fut = async move { - if path.starts_with("/api") { - let whole_body = - tokio::time::timeout(Duration::from_secs(10), body.collect()).await??; - let bytes = whole_body.to_bytes(); - let body_string: Option = String::from_utf8(bytes.to_vec()).ok(); - Ok(api::handle(&path, headers, body_string).await) - } else if path.starts_with("/direct") { - let short = path.replace("/direct/", ""); - if let Ok(long) = get_short_link(&short).await { - let response = HttpResponse::builder() - .status(StatusCode::FOUND) - .header("Location", long) - .body(Full::new(Bytes::from(""))) - .unwrap(); - Ok(response) - } else { - let response = HttpResponse::builder() - .status(StatusCode::NOT_FOUND) - .body(Full::new(Bytes::from("Short link not found"))) - .unwrap(); - Ok(response) - } - } else { - // Not a WebSocket, /api, or /direct, so it's a 404 - let response = HttpResponse::builder() - .status(StatusCode::NOT_FOUND) - .body(Full::new(Bytes::from("Not Found"))) - .unwrap(); - Ok(response) - } - }; + let mut config = ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(cert_chain, key)?; - Box::pin(fut.map_err(|err: color_eyre::eyre::ErrReport| { - io::Error::new( - io::ErrorKind::Other, - format!("Error in API request: {}", err), - ) - })) - } -} + config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; -// --- WebSocketService for HTTP/1.1 --- + let addr = format!("0.0.0.0:{port}"); + log!(" Server on {}", addr); -#[derive(Clone)] -struct WebSocketService { - _peer_addr: SocketAddr, -} - -impl Service> for WebSocketService { - type Response = HttpResponse>; - type Error = io::Error; - type Future = Pin> + Send>>; - - fn poll_ready( - &mut self, - _cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - std::task::Poll::Ready(std::io::Result::Ok(())) - } - - fn call(&mut self, req: HttpRequest) -> Self::Future { - let (parts, body) = req.into_parts(); - let method = parts.method.clone(); - let path = parts.uri.path().to_string(); - let headers = parts.headers.clone(); - - let fut = async move { - let is_websocket_upgrade = path.starts_with("/ws") - && method == Method::GET - && headers - .get("connection") - .map(|v| v.to_str().unwrap_or("").contains("Upgrade")) - .unwrap_or(false) - && headers.get("upgrade").map(|v| v.to_str().unwrap_or("")) == Some("websocket"); - - if is_websocket_upgrade { - log!("Attempting WebSocket upgrade on /ws"); - if let Some(sec_websocket_key) = headers.get("sec-websocket-key") { - let sec_websocket_key_str = - sec_websocket_key.to_str().unwrap_or("").to_string(); - let sec_websocket_accept = calculate_accept_key(&sec_websocket_key_str); - - let response = HttpResponse::builder() - .status(StatusCode::SWITCHING_PROTOCOLS) - .header("Upgrade", "websocket") - .header("Connection", "Upgrade") - .header("Sec-WebSocket-Accept", sec_websocket_accept) - .body(Full::new(Bytes::from(""))) - .unwrap(); - - let req_for_upgrade = HttpRequest::from_parts(parts, body); - let upgrades = upgrade::on(req_for_upgrade); - socket::handle(path, upgrades); - Ok(response) - } else { - log!("No Sec-WebSocket-Key found in request headers"); - let response = HttpResponse::builder() - .status(StatusCode::BAD_REQUEST) - .body(Full::new(Bytes::from("Missing Sec-WebSocket-Key"))) - .unwrap(); - Ok(response) - } - } else { - let response = HttpResponse::builder() - .status(StatusCode::NOT_FOUND) - .body(Full::new(Bytes::from( - "Not Found: Use the API server for non-WebSocket requests.", - ))) - .unwrap(); - Ok(response) - } - }; - - Box::pin(fut.map_err(|_err: color_eyre::eyre::ErrReport| { - io::Error::new(io::ErrorKind::Other, "Error in WebSocket request") - })) - } -} - -async fn run_api_server(port: u16) -> bool { - let mut ip = "0.0.0.0".to_string(); - for iface in pnet::datalink::interfaces() { - if let Some(ip_net) = iface.ips.iter().find(|ip_net| { - ip_net.is_ipv4() - && (ip_net.ip().to_string().starts_with("10.") - || ip_net.ip().to_string().starts_with("192.")) - }) { - ip = ip_net.ip().to_string(); - break; - } - } - - let addr = SocketAddr::new(ip.parse().unwrap(), port); - let listener_res = TcpListener::bind(format!("0.0.0.0:{}", port)).await; - if let Err(e) = listener_res { - log!( - "[FATAL] Failed to bind API server to port {}: {:?}", - port, - e - ); - return false; - } - let listener = listener_res.unwrap(); - log!("API Server (HTTP/2) listening on {}:{}", ip, port); - - tokio::spawn(async move { - loop { - if let Ok((stream, addr)) = listener.accept().await { - let service = ApiService { _peer_addr: addr }; - let io = TokioIo::new(stream); - tokio::spawn(async move { - let conn = http2::Builder::new(TokioExecutor::new()) - .serve_connection(io, TowerToHyperService::new(service)); - if let Err(err) = conn.await { - if !err - .to_string() - .starts_with("error shutting down connection") - { - log!("API server connection error: {:?}", err); - } - } - }); - } - } - }); - - true -} - -async fn run_websocket_server(port: u16) -> bool { - let mut ip = "0.0.0.0".to_string(); - for iface in pnet::datalink::interfaces() { - if let Some(ip_net) = iface.ips.iter().find(|ip_net| { - ip_net.is_ipv4() - && (ip_net.ip().to_string().starts_with("10.") - || ip_net.ip().to_string().starts_with("192.")) - }) { - ip = ip_net.ip().to_string(); - break; - } - } - - let addr = SocketAddr::new(ip.parse().unwrap(), port); - let listener_res = TcpListener::bind(format!("0.0.0.0:{}", port)).await; - if let Err(e) = listener_res { - log!( - "[FATAL] Failed to bind WebSocket server to port {}: {:?}", - port, - e - ); - return false; - } - let listener = listener_res.unwrap(); - log!("WebSocket Server (HTTP/1.1) listening on {}:{}", ip, port); - - tokio::spawn(async move { - loop { - if let Ok((stream, addr)) = listener.accept().await { - let service = WebSocketService { _peer_addr: addr }; - let io = TokioIo::new(stream); - tokio::spawn(async move { - let conn = http1::Builder::new() - .preserve_header_case(true) - .title_case_headers(true) - .serve_connection(io, TowerToHyperService::new(service)) - .with_upgrades(); - if let Err(err) = conn.await { - if !err - .to_string() - .starts_with("error shutting down connection") - { - log!("WebSocket server connection error: {:?}", err); - } - } - }); - } - } - }); - - true -} - -// --- Main Start function --- - -pub async fn start(api_port: u16, ws_port: u16) { - // We are not using TLS for this setup as per the request's focus on splitting protocols. - // The previous TLS loading logic is removed for simplicity. - tokio::spawn(run_api_server(api_port)); - tokio::spawn(run_websocket_server(ws_port)); -} - -fn calculate_accept_key(key: &str) -> String { - let websocket_guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - let mut sha1 = Sha1::new(); - sha1.update(key.as_bytes()); - sha1.update(websocket_guid.as_bytes()); - let result = sha1.finalize(); - STANDARD.encode(result) -======= -async fn direct_handler(Path(short): Path) -> impl IntoResponse { - match get_short_link(&short).await { - Ok(long) => Redirect::temporary(&long), - Err(_) => Redirect::temporary("https://tensamin.net"), - } -} - -async fn fallback_handler( - OriginalUri(uri): OriginalUri, - headers: axum::http::HeaderMap, - body: Body, -) -> impl IntoResponse { - let path = uri.path().to_string(); - - let whole_body = tokio::time::timeout( - Duration::from_secs(10), - axum::body::to_bytes(body, 1024 * 1024 * 10), - ) - .await; - - let body_string = match whole_body { - Ok(Ok(bytes)) => String::from_utf8(bytes.to_vec()).ok(), - _ => None, - }; - - api::handle(&path, headers, body_string).await -} - -async fn run_http_server(port: u16, app: Router) -> bool { - let ip = find_local_ip(); - let listener = match TcpListener::bind(format!("0.0.0.0:{}", port)).await { - Ok(l) => l, - Err(e) => { - log!("Failed to bind to port {}: {:?}", port, e); - return false; - } - }; - - log!( - "Standard Server listening for HTTP and WS on {}:{}", - ip, - port - ); - - axum::serve( - listener, - app.into_make_service_with_connect_info::(), - ) - .await - .map(|_| true) - .unwrap_or_else(|e| { - log!("Server error: {}", e); - false + HttpServer::new(move || { + App::new() + .route("/api/{path:.*}", web::to(api_handler)) + .route("/direct/{path:.*}", web::to(direct_handler)) + .route("/ws/{path:.*}", web::get().to(ws_handler)) }) + .bind_rustls_0_23(addr, config)? + .run() + .await?; + + Ok(()) +} +async fn direct_handler(req: HttpRequest) -> impl Responder { + let path = req.uri().path().to_string(); + let short = path.replace("/direct/", ""); + + if let Ok(long) = get_short_link(&short).await { + HttpResponse::TemporaryRedirect() + .append_header((header::LOCATION, long)) + .finish() + } else { + HttpResponse::TemporaryRedirect() + .append_header((header::LOCATION, "https://tensamin.net")) + .finish() + } +} +async fn ws_handler( + req: HttpRequest, + stream: web::Payload, + path: web::Path, +) -> Result { + let path = path.into_inner(); + println!("WS handler reached: {}", path); + + ws::start(socket::WsSession::new(path), &req, stream) } -fn find_local_ip() -> String { - for iface in pnet::datalink::interfaces() { - let iface: NetworkInterface = iface; - if !iface.ips.is_empty() { - let ipsv = format!("{}", iface.ips[0]); - let ips: &str = ipsv.split('/').next().unwrap(); - if ips.starts_with("10.") || ips.starts_with("192.") { - return ips.to_string(); - } - } - } - "0.0.0.0".to_string() ->>>>>>> 7f78c8669b36cbe39755d69cccd6971e56e10290 +async fn api_handler(req: HttpRequest, body: web::Bytes) -> HttpResponse { + let path = req.uri().path().to_string(); + let body_string = String::from_utf8_lossy(&body).to_string(); + + api::handle(&path, Some(body_string)).await } diff --git a/src/server/socket.rs b/src/server/socket.rs old mode 100644 new mode 100755 index 25a00f7..7d04b26 --- a/src/server/socket.rs +++ b/src/server/socket.rs @@ -1,99 +1,129 @@ use std::sync::Arc; +use std::time::{Duration, Instant}; -use axum::extract::ws::{Message, Utf8Bytes, WebSocket}; -use futures::StreamExt; +use actix::{Actor, ActorContext, AsyncContext, StreamHandler}; +use actix_web_actors::ws; use crate::data::communication::{CommunicationType, CommunicationValue}; use crate::log; use crate::server::omikron_connection::OmikronConnection; -pub fn handle(path: String, upgrades: WebSocket) { - tokio::spawn(async move { - log!( - "[ws] Spawning new task to handle WebSocket upgrade for path: {}", - path - ); - log!("[ws] WebSocket upgrade successful for path: {}", path); +const IDLE_TIMEOUT: Duration = Duration::from_secs(30); - log!( - "[ws] WebSocket handshake successful, handling connection for {}", - path - ); +use actix::Message; - let (writer, reader) = upgrades.split(); - if path == "/ws/omikron" { - let connection = OmikronConnection::new(writer, reader); - tokio::spawn(start_connecteable_handler(connection)); - } +#[derive(Message)] +#[rtype(result = "()")] +pub struct WsSendMessage(pub String); - log!( - "[ws] WebSocket handling task for path: {} is finished.", - path - ); - }); +impl actix::Handler for WsSession { + type Result = (); + + fn handle(&mut self, msg: WsSendMessage, ctx: &mut Self::Context) { + ctx.text(msg.0); + } } -pub async fn start_connecteable_handler(connection: Arc) { - use futures::SinkExt; - use tokio::time::Duration; - const IDLE_TIMEOUT: Duration = Duration::from_secs(30); +pub struct WsSession { + path: String, + last_heartbeat: Instant, + omikron: Option>, +} - log!("[ws_handler] Starting connection handler loop."); - loop { - let mut receiver_guard = connection.receiver.write().await; - - match tokio::time::timeout(IDLE_TIMEOUT, receiver_guard.next()).await { - Ok(Some(Ok(msg))) => { - drop(receiver_guard); - - match msg { - Message::Text(text) => { - let conn_clone = connection.clone(); - tokio::spawn(async move { - conn_clone.handle_message(text.to_string()).await; - }); - } - Message::Close(_) => { - log!("[ws_handler] Received 'Close' message. Breaking loop."); - break; - } - Message::Pong(_) => { - log!("[ws_handler] Received 'Pong'. Connection is alive."); - } - _ => { - log!("[ws_handler] Received unhandled message type."); - } +impl WsSession { + pub fn new(path: String) -> Self { + Self { + path, + last_heartbeat: Instant::now(), + omikron: None, + } + } + + fn start_heartbeat(&self, ctx: &mut ws::WebsocketContext) { + ctx.run_interval(Duration::from_secs(5), |act, ctx| { + if Instant::now().duration_since(act.last_heartbeat) > IDLE_TIMEOUT { + log!("[ws_handler] Heartbeat failed. Disconnecting."); + ctx.close(None); + ctx.stop(); + return; + } + + let ping = CommunicationValue::new(CommunicationType::ping) + .to_json() + .to_string(); + + ctx.text(ping); + }); + } +} + +impl Actor for WsSession { + type Context = ws::WebsocketContext; + + fn started(&mut self, ctx: &mut Self::Context) { + self.start_heartbeat(ctx); + + if self.path == "omikron" { + let addr = ctx.address(); + let connection = OmikronConnection::new(addr); + self.omikron = Some(connection); + } + } + + fn stopped(&mut self, _: &mut Self::Context) { + log!( + "[ws] WebSocket handling task for path: {} is finished.", + self.path + ); + + if let Some(conn) = &self.omikron { + let conn = conn.clone(); + actix_rt::spawn(async move { + conn.handle_close().await; + }); + } + } +} +impl StreamHandler> for WsSession { + fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { + match msg { + Ok(ws::Message::Text(text)) => { + self.last_heartbeat = Instant::now(); + + if let Some(conn) = &self.omikron { + let conn_clone = conn.clone(); + actix_rt::spawn(async move { + conn_clone.handle_message(text.to_string()).await; + }); } } - Ok(Some(Err(e))) => { - log!("[ERROR] WS Error: {}. Breaking loop.", e); - break; + + Ok(ws::Message::Ping(msg)) => { + self.last_heartbeat = Instant::now(); + ctx.pong(&msg); } - Ok(_) => { - log!("[ws_handler] WebSocket stream closed by peer. Breaking loop."); - break; + + Ok(ws::Message::Pong(_)) => { + self.last_heartbeat = Instant::now(); + log!("[ws_handler] Received Pong. Connection alive."); } - Err(_) => { - drop(receiver_guard); - log!("[ws_handler] Timeout: Dropped receiver lock. Sending a ping."); - let mut sender = connection.sender.write().await; - log!("[ws_handler] Acquired sender lock for ping."); - if let Err(e) = sender - .send(Message::Text(Utf8Bytes::from( - CommunicationValue::new(CommunicationType::ping) - .to_json() - .to_string(), - ))) - .await - { - log!("[ERROR] Failed to send ping: {}. Closing connection.", e); - break; - } - log!("[ws_handler] Ping sent successfully."); + + Ok(ws::Message::Close(reason)) => { + log!("[ws_handler] Received Close. Disconnecting."); + ctx.close(reason); + ctx.stop(); + } + + Ok(ws::Message::Binary(_)) => { + log!("[ws_handler] Binary message ignored."); + } + + Err(e) => { + log!("[ERROR] WS Error: {}. Closing.", e); + ctx.stop(); } + + _ => {} } } - log!("[ws_handler] Connection handler loop finished."); - connection.handle_close().await; - log!("[ws_handler] Connection closed."); } From bbe18ee759a75161974c5cc03694639183d5aee8 Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Sun, 15 Feb 2026 01:35:21 +0100 Subject: [PATCH 3/4] [Fix] Filedownloads --- src/server/api.rs | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/src/server/api.rs b/src/server/api.rs index bc240dc..f826e3e 100644 --- a/src/server/api.rs +++ b/src/server/api.rs @@ -1,21 +1,27 @@ use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; +use crate::get_public_key; use crate::server::omikron_manager::get_random_omikron; use crate::sql::sql; use crate::sql::user_online_tracker::get_iota_primary_omikron_connection; -use crate::util::file_util::load_file; -use crate::{get_public_key, log}; use crate::{ sql::sql::{get_by_user_id, get_omikron_by_id}, util::crypto_helper::public_key_to_base64, }; use actix_web::HttpResponse; -use actix_web::body::BoxBody; -use actix_web::http::StatusCode; +use actix_web::http::{StatusCode, header}; use base64::Engine as _; use json::JsonValue; use json::number::Number; pub async fn handle(path: &str, body_string: Option) -> HttpResponse { + if path == "OPTIONS" { + return HttpResponse::Ok() + .insert_header(("Access-Control-Allow-Origin", "*")) + .insert_header(("Access-Control-Allow-Methods", "GET, POST, OPTIONS")) + .insert_header(("Access-Control-Allow-Headers", "*")) + .finish(); + } + let path_parts: Vec<&str> = path.split("/").filter(|s| !s.is_empty()).collect(); let _body: Option = if body_string.is_some() { @@ -27,11 +33,28 @@ pub async fn handle(path: &str, body_string: Option) -> HttpResponse { } else { None }; - log!("Path parts: {:?}", path_parts); + let (status, body_text) = match path_parts.as_slice() { ["api", "download", "iota_frontend"] => { - let file = load_file("downloads", "iota_frontend.zip"); - (StatusCode::OK, file) + let file_path = "downloads/[iota_frontend].zip"; + + match std::fs::read(file_path) { + Ok(file_bytes) => { + return HttpResponse::Ok() + .insert_header(("Access-Control-Allow-Origin", "*")) + .insert_header(("Content-Type", "application/zip")) + .insert_header(( + "Content-Disposition", + "attachment; filename=\"iota_frontend.zip\"", + )) + .body(file_bytes); + } + Err(_) => { + return HttpResponse::NotFound() + .insert_header(("Access-Control-Allow-Origin", "*")) + .body("File not found"); + } + } } ["api", "get", "omikron"] => { if let Ok(omikron_conn) = get_random_omikron().await { @@ -205,9 +228,13 @@ pub async fn handle(path: &str, body_string: Option) -> HttpResponse { .to_string(), ), }; - let body_bytes = body_text.to_string().into_bytes(); - let body = BoxBody::new(body_bytes.clone()); - HttpResponse::new(status).set_body(body) + let body_bytes = body_text.into_bytes(); + + HttpResponse::build(status) + .insert_header((header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")) + .insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, "*")) + .insert_header((header::ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, OPTIONS")) + .body(body_bytes) } pub fn bad_request() -> (StatusCode, String) { From 0af5254c405bf59552e14137ae4725ac2aaf3f8c Mon Sep 17 00:00:00 2001 From: Alex Emmet <111742636+Alex-Emmet@users.noreply.github.com> Date: Sun, 15 Feb 2026 12:45:16 +0100 Subject: [PATCH 4/4] [Fix] logging & not sending empty data --- src/server/omikron_connection.rs | 35 ++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/src/server/omikron_connection.rs b/src/server/omikron_connection.rs index b44d1b4..d6eb54a 100644 --- a/src/server/omikron_connection.rs +++ b/src/server/omikron_connection.rs @@ -213,12 +213,14 @@ impl OmikronConnection { // ONLINE STATUS TRACKING if cv.is_type(CommunicationType::user_connected) { + log_in!(PrintType::Omega, "User connected"); if let Some(user_id) = cv.get_data(DataTypes::user_id).and_then(|v| v.as_i64()) { user_online_tracker::track_user_status(user_id, UserStatus::online, omikron_id); } return; } if cv.is_type(CommunicationType::user_disconnected) { + log_in!(PrintType::Omega, "User disconnected"); if let Some(user_id) = cv.get_data(DataTypes::user_id).and_then(|v| v.as_i64()) { if let Some(status) = user_online_tracker::get_user_status(user_id) { user_online_tracker::track_user_status( @@ -260,6 +262,7 @@ impl OmikronConnection { return; } if cv.is_type(CommunicationType::iota_disconnected) { + log_in!(PrintType::Omega, "IOTA disconnected"); if let Some(iota_id) = cv.get_data(DataTypes::iota_id).and_then(|v| v.as_i64()) { let iota_offline = user_online_tracker::untrack_iota_connection(iota_id, omikron_id); @@ -320,7 +323,7 @@ impl OmikronConnection { let mut response = CommunicationValue::new(CommunicationType::get_user_data) .with_id(cv.get_id()) - .add_data_str(DataTypes::username, username) + .add_data_str(DataTypes::username, username.clone()) .add_data_str(DataTypes::public_key, public_key) .add_data(DataTypes::user_id, JsonValue::Number(Number::from(id))) .add_data( @@ -337,13 +340,21 @@ impl OmikronConnection { ); if let Some(display) = display { - response = response.add_data_str(DataTypes::display, display); + if display.is_empty() { + response = response.add_data_str(DataTypes::display, username); + } else { + response = response.add_data_str(DataTypes::display, display); + } } if let Some(status) = status { - response = response.add_data_str(DataTypes::status, status); + if !status.is_empty() { + response = response.add_data_str(DataTypes::status, status); + } } if let Some(about) = about { - response = response.add_data_str(DataTypes::about, about); + if !about.is_empty() { + response = response.add_data_str(DataTypes::about, about); + } } if let Some(avatar) = avatar { response = @@ -405,7 +416,7 @@ impl OmikronConnection { let mut response = CommunicationValue::new(CommunicationType::get_user_data) .with_id(cv.get_id()) - .add_data_str(DataTypes::username, username) + .add_data_str(DataTypes::username, username.clone()) .add_data_str(DataTypes::public_key, public_key) .add_data(DataTypes::user_id, JsonValue::Number(Number::from(id))) .add_data( @@ -422,13 +433,21 @@ impl OmikronConnection { ); if let Some(display) = display { - response = response.add_data_str(DataTypes::display, display); + if display.is_empty() { + response = response.add_data_str(DataTypes::display, username); + } else { + response = response.add_data_str(DataTypes::display, display); + } } if let Some(status) = status { - response = response.add_data_str(DataTypes::status, status); + if !status.is_empty() { + response = response.add_data_str(DataTypes::status, status); + } } if let Some(about) = about { - response = response.add_data_str(DataTypes::about, about); + if !about.is_empty() { + response = response.add_data_str(DataTypes::about, about); + } } if let Some(avatar) = avatar { response =