(port: u16, app: AppHandle, mut shutdown_rx: tokio::sync::watch::Receiver<bool>)
| 179 | /// Simple HTTP server for serving hosts files (async with graceful shutdown) |
| 180 | #[cfg(feature = "gui")] |
| 181 | async fn start_http_server(port: u16, app: AppHandle, mut shutdown_rx: tokio::sync::watch::Receiver<bool>) { |
| 182 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; |
| 183 | use tokio::net::TcpListener; |
| 184 | |
| 185 | let addr = format!("0.0.0.0:{}", port); |
| 186 | let listener = match TcpListener::bind(&addr).await { |
| 187 | Ok(l) => l, |
| 188 | Err(e) => { |
| 189 | let _ = app.emit( |
| 190 | "server-log", |
| 191 | log_payload("server.startFail", Some(serde_json::json!({"error": e.to_string()})), "error"), |
| 192 | ); |
| 193 | return; |
| 194 | } |
| 195 | }; |
| 196 | |
| 197 | let _ = app.emit( |
| 198 | "server-log", |
| 199 | log_payload("server.httpStarted", Some(serde_json::json!({"port": port})), "success"), |
| 200 | ); |
| 201 | |
| 202 | loop { |
| 203 | tokio::select! { |
| 204 | result = listener.accept() => { |
| 205 | match result { |
| 206 | Ok((mut stream, _)) => { |
| 207 | let mut buf = [0u8; 4096]; |
| 208 | let _ = stream.read(&mut buf).await; |
| 209 | let request = String::from_utf8_lossy(&buf); |
| 210 | |
| 211 | let path = request |
| 212 | .lines() |
| 213 | .next() |
| 214 | .and_then(|line| line.split_whitespace().nth(1)) |
| 215 | .unwrap_or("/"); |
| 216 | |
| 217 | let (status, content_type, body) = handle_http_request(path); |
| 218 | |
| 219 | let response = format!( |
| 220 | "HTTP/1.1 {}\r\nContent-Type: {}\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: *\r\n\r\n{}", |
| 221 | status, |
| 222 | content_type, |
| 223 | body.len(), |
| 224 | body |
| 225 | ); |
| 226 | let _ = stream.write_all(response.as_bytes()).await; |
| 227 | } |
| 228 | Err(_) => break, |
| 229 | } |
| 230 | } |
| 231 | _ = shutdown_rx.changed() => { |
| 232 | break; |
| 233 | } |
| 234 | } |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | /// Handle HTTP request and return (status, content_type, body) |
no test coverage detected