use std::net::SocketAddr;
use acktor::{Actor, Address, Context, ErrorReport, JoinHandle};
use serde::{Deserialize, Serialize};
use tokio::net::TcpListener;
use tracing::{Instrument, error, info};
use crate::memory::MemoryManager;
mod router;
pub use router::build_router;
pub mod error;
pub use error::HttpServerError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub host: String,
pub port: u16,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
host: "0.0.0.0".to_string(),
port: 8000,
}
}
}
#[derive(Clone)]
pub struct HttpServerState {
pub memory_manager: Address<MemoryManager>,
}
impl HttpServerState {
pub fn new(memory_manager: Address<MemoryManager>) -> Self {
Self { memory_manager }
}
}
pub struct HttpServer {
config: ServerConfig,
memory_manager: Address<MemoryManager>,
serve_handle: Option<JoinHandle<()>>,
}
impl HttpServer {
pub fn new(config: ServerConfig, memory_manager: Address<MemoryManager>) -> Self {
Self {
config,
memory_manager,
serve_handle: None,
}
}
}
impl Actor for HttpServer {
type Context = Context<Self>;
type Error = HttpServerError;
async fn post_start(&mut self, _ctx: &mut Self::Context) -> Result<(), HttpServerError> {
let addr_str = format!("{}:{}", self.config.host, self.config.port);
let addr: SocketAddr = addr_str
.parse()
.map_err(|source| HttpServerError::InvalidAddr {
addr: addr_str.clone(),
source,
})?;
let listener = TcpListener::bind(addr)
.await
.map_err(|source| HttpServerError::Bind {
addr: addr_str.clone(),
source,
})?;
let app = build_router(HttpServerState::new(self.memory_manager.clone()));
let handle = tokio::spawn(
async move {
if let Err(e) = axum::serve(listener, app).await {
error!("Could not start HTTP server: {}", e.report());
}
}
.in_current_span(),
);
self.serve_handle = Some(handle);
info!("HttpServer is listening on {}", addr);
Ok(())
}
async fn post_stop(&mut self, _ctx: &mut Self::Context) -> Result<(), HttpServerError> {
if let Some(handle) = self.serve_handle.take() {
handle.abort();
}
info!("HttpServer is stopped");
Ok(())
}
}