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