CLI HTTP server (no AppHandle dependency)
(port: u16, mut shutdown_rx: tokio::sync::watch::Receiver<bool>)
| 190 | |
| 191 | /// CLI HTTP server (no AppHandle dependency) |
| 192 | async fn start_cli_http_server(port: u16, mut shutdown_rx: tokio::sync::watch::Receiver<bool>) { |
| 193 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; |
| 194 | use tokio::net::TcpListener; |
| 195 | |
| 196 | let addr = format!("0.0.0.0:{}", port); |
| 197 | let listener = match TcpListener::bind(&addr).await { |
| 198 | Ok(l) => l, |
| 199 | Err(e) => { |
| 200 | cli_log(&format!("❌ HTTP 服务启动失败: {}", e)); |
| 201 | return; |
| 202 | } |
| 203 | }; |
| 204 | |
| 205 | loop { |
| 206 | tokio::select! { |
| 207 | result = listener.accept() => { |
| 208 | match result { |
| 209 | Ok((mut stream, _)) => { |
| 210 | let mut buf = [0u8; 4096]; |
| 211 | let _ = stream.read(&mut buf).await; |
| 212 | let request = String::from_utf8_lossy(&buf); |
| 213 | |
| 214 | let path = request |
| 215 | .lines() |
| 216 | .next() |
| 217 | .and_then(|line| line.split_whitespace().nth(1)) |
| 218 | .unwrap_or("/"); |
| 219 | |
| 220 | let (status, content_type, body) = services::handle_http_request(path); |
| 221 | |
| 222 | let response = format!( |
| 223 | "HTTP/1.1 {}\r\nContent-Type: {}\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: *\r\n\r\n{}", |
| 224 | status, content_type, body.len(), 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 | /// Print log with timestamp to stdout |
| 239 | fn cli_log(msg: &str) { |
no test coverage detected