- Added custom logging macros (log_error!, log_warn!, log_info!, log_debug!) - Implemented LogLevel enum and LOG_LEVEL static for runtime level control - Exported macros with #[macro_export] for use across all modules - Replaced all log:: calls in websocket_client.rs, health.rs, and version.rs - Removed obsolete test code referencing old log crate - Removed log dependency from Cargo.toml - Fixed useless comparison warning in test - Binary size: still 1.5M (no change - log was lightweight) - Tests: 65 passing, 0 warnings This completes the removal of 9 dependencies (thiserror, hostname, chrono, base64, rand, ureq, tiny_http, anyhow, log). Dependency count: 19 → 10. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
118 lines
4.3 KiB
Rust
118 lines
4.3 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use std::sync::Arc;
|
|
use std::time::Instant;
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
use tokio::net::TcpListener;
|
|
|
|
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct HealthStatus {
|
|
pub status: String,
|
|
pub version: String,
|
|
pub uptime_seconds: u64,
|
|
}
|
|
|
|
/// Start a simple health check HTTP server using raw TCP
|
|
pub async fn start_health_server(port: u16) -> Result<()> {
|
|
let start_time = Arc::new(Instant::now());
|
|
let addr = format!("0.0.0.0:{}", port);
|
|
|
|
crate::log_info!("Starting health endpoint on {}", addr);
|
|
let listener = TcpListener::bind(&addr).await?;
|
|
|
|
loop {
|
|
match listener.accept().await {
|
|
Ok((mut socket, _)) => {
|
|
let start_time = Arc::clone(&start_time);
|
|
|
|
tokio::spawn(async move {
|
|
let mut buffer = [0u8; 1024];
|
|
|
|
// Read the HTTP request
|
|
if let Ok(n) = socket.read(&mut buffer).await {
|
|
if n > 0 {
|
|
let request = String::from_utf8_lossy(&buffer[..n]);
|
|
|
|
// Check if this is a GET request to /health
|
|
if request.starts_with("GET /health") {
|
|
let uptime = start_time.elapsed().as_secs();
|
|
let status = HealthStatus {
|
|
status: "healthy".to_string(),
|
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
|
uptime_seconds: uptime,
|
|
};
|
|
|
|
let json = serde_json::to_string(&status).unwrap_or_else(|_| {
|
|
r#"{"status":"error","message":"Failed to serialize health status"}"#
|
|
.to_string()
|
|
});
|
|
|
|
let response = format!(
|
|
"HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}",
|
|
json.len(),
|
|
json
|
|
);
|
|
|
|
let _ = socket.write_all(response.as_bytes()).await;
|
|
} else {
|
|
// 404 for other paths
|
|
let response = "HTTP/1.1 404 Not Found\r\nContent-Length: 9\r\n\r\nNot found";
|
|
let _ = socket.write_all(response.as_bytes()).await;
|
|
}
|
|
}
|
|
}
|
|
|
|
let _ = socket.shutdown().await;
|
|
});
|
|
}
|
|
Err(e) => {
|
|
crate::log_warn!("Failed to accept health check connection: {}", e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_health_status_serialization() {
|
|
let status = HealthStatus {
|
|
status: "healthy".to_string(),
|
|
version: "0.1.0".to_string(),
|
|
uptime_seconds: 42,
|
|
};
|
|
|
|
let json = serde_json::to_string(&status).unwrap();
|
|
assert!(json.contains(r#""status":"healthy""#));
|
|
assert!(json.contains(r#""version":"0.1.0""#));
|
|
assert!(json.contains(r#""uptime_seconds":42"#));
|
|
}
|
|
|
|
#[test]
|
|
fn test_health_status_deserialization() {
|
|
let json = r#"{"status":"healthy","version":"0.1.0","uptime_seconds":42}"#;
|
|
let status: HealthStatus = serde_json::from_str(json).unwrap();
|
|
assert_eq!(status.status, "healthy");
|
|
assert_eq!(status.version, "0.1.0");
|
|
assert_eq!(status.uptime_seconds, 42);
|
|
}
|
|
|
|
#[test]
|
|
fn test_health_status_clone() {
|
|
let status = HealthStatus {
|
|
status: "healthy".to_string(),
|
|
version: "0.1.0".to_string(),
|
|
uptime_seconds: 42,
|
|
};
|
|
let cloned = status.clone();
|
|
assert_eq!(status.status, cloned.status);
|
|
assert_eq!(status.version, cloned.version);
|
|
assert_eq!(status.uptime_seconds, cloned.uptime_seconds);
|
|
}
|
|
|
|
// Note: start_health_server is tested manually/via integration tests
|
|
// Unit testing it requires complex async server mocking which is not practical
|
|
}
|