kotoba_server_core/
server.rs1use axum::{
4 Router,
5 extract::Request,
6 http::{HeaderMap, StatusCode},
7 response::{IntoResponse, Response},
8};
9use hyper::server::conn::http1;
10use hyper_util::rt::TokioIo;
11use std::net::SocketAddr;
12use tokio::net::TcpListener;
13use tower::Service;
14use tower_http::cors::{CorsLayer, Any};
15use tower_http::trace::TraceLayer;
16
17use crate::{ServerError, Result};
18
19#[derive(Debug, Clone)]
21pub struct ServerConfig {
22 pub host: String,
23 pub port: u16,
24 pub cors_enabled: bool,
25 pub tracing_enabled: bool,
26}
27
28impl Default for ServerConfig {
29 fn default() -> Self {
30 Self {
31 host: "127.0.0.1".to_string(),
32 port: 8100,
33 cors_enabled: true,
34 tracing_enabled: true,
35 }
36 }
37}
38
39#[derive(Debug)]
41pub struct ServerBuilder {
42 config: ServerConfig,
43 router: Router,
44}
45
46impl ServerBuilder {
47 pub fn new() -> Self {
48 Self {
49 config: ServerConfig::default(),
50 router: Router::new(),
51 }
52 }
53
54 pub fn config(mut self, config: ServerConfig) -> Self {
55 self.config = config;
56 self
57 }
58
59 pub fn router(mut self, router: Router) -> Self {
60 self.router = router;
61 self
62 }
63
64 pub fn host(mut self, host: impl Into<String>) -> Self {
65 self.config.host = host.into();
66 self
67 }
68
69 pub fn port(mut self, port: u16) -> Self {
70 self.config.port = port;
71 self
72 }
73
74 pub fn cors_enabled(mut self, enabled: bool) -> Self {
75 self.config.cors_enabled = enabled;
76 self
77 }
78
79 pub fn tracing_enabled(mut self, enabled: bool) -> Self {
80 self.config.tracing_enabled = enabled;
81 self
82 }
83
84 pub fn build(self) -> Result<HttpServer> {
85 let mut router = self.router;
86
87 if self.config.cors_enabled {
89 router = router.layer(
90 CorsLayer::new()
91 .allow_origin(Any)
92 .allow_methods(Any)
93 .allow_headers(Any),
94 );
95 }
96
97 if self.config.tracing_enabled {
99 router = router.layer(TraceLayer::new_for_http());
100 }
101
102 Ok(HttpServer {
103 config: self.config,
104 router,
105 })
106 }
107}
108
109#[derive(Debug)]
111pub struct HttpServer {
112 config: ServerConfig,
113 router: Router,
114}
115
116impl HttpServer {
117 pub fn builder() -> ServerBuilder {
118 ServerBuilder::new()
119 }
120
121 pub async fn serve(self) -> Result<()> {
122 let addr = format!("{}:{}", self.config.host, self.config.port)
123 .parse::<SocketAddr>()
124 .map_err(|e| ServerError::Config(format!("Invalid address: {}", e)))?;
125
126 let listener = TcpListener::bind(&addr).await?;
127 tracing::info!("🌐 Kotoba HTTP Server listening on {}", listener.local_addr()?);
128
129 loop {
130 let (stream, _) = listener.accept().await?;
131 let io = TokioIo::new(stream);
132 let router = self.router.clone();
133
134 let tower_service = hyper_util::service::TowerToHyperService::new(router);
135 tokio::task::spawn(async move {
136 if let Err(err) = http1::Builder::new()
137 .serve_connection(io, tower_service)
138 .await
139 {
140 tracing::error!("Error serving connection: {:?}", err);
141 }
142 });
143 }
144 }
145
146 pub fn router(&self) -> &Router {
147 &self.router
148 }
149
150 pub fn config(&self) -> &ServerConfig {
151 &self.config
152 }
153}