1use http_body_util::Full;
7use hyper::body::Incoming;
8use hyper::server::conn::http1;
9use hyper::service::service_fn;
10use hyper::{Request, Response};
11use hyper_util::rt::TokioIo;
12use std::net::SocketAddr;
13use std::sync::Arc;
14use tokio::net::TcpListener;
15use tracing::{error, info};
16
17use crate::api::endpoints;
18
19use crate::config::Config;
20use crate::error::Result;
21
22pub async fn start_api_server(addr: &str, config: Arc<tokio::sync::RwLock<Config>>) -> Result<()> {
47 let addr: SocketAddr = addr.parse()?;
48 start_api_server_with_addr(addr, config).await
49}
50
51pub async fn start_api_server_with_addr(
60 addr: SocketAddr,
61 config: Arc<tokio::sync::RwLock<Config>>,
62) -> Result<()> {
63 let listener = TcpListener::bind(&addr).await?;
64
65 info!("API server listening on http://{}", addr);
66
67 loop {
68 let (stream, _) = listener.accept().await?;
69 let io = TokioIo::new(stream);
70 let config = config.clone();
71
72 tokio::task::spawn(async move {
73 let service = service_fn(move |req| {
74 let config = config.clone();
75 handle_api_request(req, config)
76 });
77
78 if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
79 error!("Error serving API connection: {:?}", err);
80 }
81 });
82 }
83}
84
85async fn handle_api_request(
89 req: Request<Incoming>,
90 config: Arc<tokio::sync::RwLock<Config>>,
91) -> anyhow::Result<Response<Full<bytes::Bytes>>> {
92 let path = req.uri().path();
96 let method = req.method();
97
98 info!("API request: {} {}", method, path);
99
100 match (method.as_str(), path) {
101 ("GET", "/config") => endpoints::handle_get_config(req, config).await,
102 ("POST", "/config") => endpoints::handle_post_config(req, config).await,
103 ("GET", "/health") => endpoints::handle_health_check(req).await,
104 _ => {
105 let response = Response::builder()
107 .status(404)
108 .body(Full::new(bytes::Bytes::from("Not Found".to_string())))
109 .unwrap();
110 Ok(response)
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_api_server_address_parsing() {
121 let addr: SocketAddr = "127.0.0.1:8081".parse().unwrap();
122 assert_eq!(addr.port(), 8081);
123 assert_eq!(addr.ip().to_string(), "127.0.0.1");
124 }
125}