AkurAI Build
Menu

Public repository / master

AkurAI-DNS / src/health.rs

Latest change 26c909314920ab20df94cca011f96d1fa6df9138 - Add loopback health listener by agent

use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use std::thread;

use tracing::{error, info, warn};

const DEFAULT_PORT: u16 = 8053;
const BODY: &str = concat!(
    r#"{"app":"akurai-dns","version":""#,
    env!("CARGO_PKG_VERSION"),
    r#"","status":"ok"}"#,
);

fn response() -> String {
    format!(
        "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
        BODY.len(),
        BODY
    )
}

fn handle(mut stream: TcpStream) {
    let mut request = [0_u8; 1024];
    if let Err(err) = stream.read(&mut request) {
        warn!(error = %err, "Health request read failed");
        return;
    }

    if let Err(err) = stream.write_all(response().as_bytes()) {
        warn!(error = %err, "Health response write failed");
    }
}

pub fn start() {
    let port = std::env::var("MONITOR_HEALTH_PORT")
        .ok()
        .and_then(|value| value.parse().ok())
        .unwrap_or(DEFAULT_PORT);

    let address = format!("127.0.0.1:{port}");
    let listener = match TcpListener::bind(&address) {
        Ok(listener) => listener,
        Err(err) => {
            error!(address = %address, error = %err, "Health listener failed to bind");
            return;
        }
    };

    info!(address = %address, "Health listener started");
    thread::spawn(move || {
        for connection in listener.incoming() {
            match connection {
                Ok(stream) => handle(stream),
                Err(err) => error!(error = %err, "Health listener accept failed"),
            }
        }
    });
}

#[cfg(test)]
mod tests {
    use super::response;

    #[test]
    fn response_has_monitor_health_shape() {
        let response = response();
        let (headers, body) = response.split_once("\r\n\r\n").unwrap();

        assert!(headers.starts_with("HTTP/1.1 200 OK\r\n"));
        assert!(headers.contains("Content-Type: application/json\r\n"));
        assert_eq!(body, super::BODY);
        assert!(body.contains(r#""app":"akurai-dns""#));
        assert!(body.contains(&format!(r#""version":"{}""#, env!("CARGO_PKG_VERSION"))));
        assert!(body.contains(r#""status":"ok""#));
    }
}