Files
hakorune/src/boxes/http_server_box.rs

386 lines
14 KiB
Rust
Raw Normal View History

/*! 🌐 HTTPServerBox - HTTP サーバー実装
*
* ## 📝
* TCP SocketBox HTTP/1.1
*
*
* ## 🛠
* ### Server Management
* - `bind(address, port)` - bind
* - `listen(backlog)` -
* - `start()` - HTTP
* - `stop()` -
*
* ### Routing & Handlers
* - `route(path, handler)` -
* - `get(path, handler)` - GET
* - `post(path, handler)` - POST
* - `put(path, handler)` - PUT
* - `delete(path, handler)` - DELETE
*
* ### Middleware & Configuration
* - `use(middleware)` -
* - `setStaticPath(path)` -
* - `setTimeout(seconds)` -
*
* ## 💡 使
* ```nyash
* // HTTP Server creation
* local server = new HTTPServerBox()
* server.bind("0.0.0.0", 8080)
*
* // Route handlers
* server.get("/", APIHandler.home)
* server.get("/api/status", APIHandler.status)
* server.post("/api/users", APIHandler.createUser)
*
* // Start server (blocking)
* print("🚀 Server starting on port 8080...")
* server.start()
* ```
*/
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, BoxCore, BoxBase};
use crate::boxes::{SocketBox, MapBox, ArrayBox};
use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox};
use crate::boxes::future::FutureBox;
use std::any::Any;
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::thread;
/// HTTP サーバーを提供するBox
#[derive(Debug)]
pub struct HTTPServerBox {
base: BoxBase,
socket: Arc<Mutex<Option<SocketBox>>>,
routes: Arc<Mutex<HashMap<String, Box<dyn NyashBox>>>>,
middleware: Arc<Mutex<Vec<Box<dyn NyashBox>>>>,
running: Arc<Mutex<bool>>,
static_path: Arc<Mutex<Option<String>>>,
timeout_seconds: Arc<Mutex<u64>>,
active_connections: Arc<Mutex<Vec<Box<dyn NyashBox>>>>,
}
impl Clone for HTTPServerBox {
fn clone(&self) -> Self {
Self {
base: BoxBase::new(), // New unique ID for clone
socket: Arc::clone(&self.socket),
routes: Arc::clone(&self.routes),
middleware: Arc::clone(&self.middleware),
running: Arc::clone(&self.running),
static_path: Arc::clone(&self.static_path),
timeout_seconds: Arc::clone(&self.timeout_seconds),
active_connections: Arc::clone(&self.active_connections),
}
}
}
impl HTTPServerBox {
pub fn new() -> Self {
Self {
base: BoxBase::new(),
socket: Arc::new(Mutex::new(None)),
routes: Arc::new(Mutex::new(HashMap::new())),
middleware: Arc::new(Mutex::new(Vec::new())),
running: Arc::new(Mutex::new(false)),
static_path: Arc::new(Mutex::new(None)),
timeout_seconds: Arc::new(Mutex::new(30)),
active_connections: Arc::new(Mutex::new(Vec::new())),
}
}
/// サーバーアドレスにバインド
pub fn bind(&self, address: Box<dyn NyashBox>, port: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let socket = SocketBox::new();
let bind_result = socket.bind(address, port);
if bind_result.to_string_box().value == "true" {
*self.socket.lock().unwrap() = Some(socket);
Box::new(BoolBox::new(true))
} else {
Box::new(BoolBox::new(false))
}
}
/// 接続待機開始
pub fn listen(&self, backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let socket_guard = self.socket.lock().unwrap();
if let Some(ref socket) = *socket_guard {
socket.listen(backlog)
} else {
Box::new(BoolBox::new(false))
}
}
/// HTTP サーバー開始(メインループ)
pub fn start(&self) -> Box<dyn NyashBox> {
*self.running.lock().unwrap() = true;
let socket_guard = self.socket.lock().unwrap();
if let Some(ref socket) = *socket_guard {
// Clone socket for the server loop
let server_socket = socket.clone();
drop(socket_guard);
println!("🚀 HTTP Server starting...");
// Main server loop
let running = Arc::clone(&self.running);
let routes = Arc::clone(&self.routes);
let active_connections = Arc::clone(&self.active_connections);
loop {
if !*running.lock().unwrap() {
break;
}
// Accept new connection
let client_result = server_socket.accept();
// Check if we got a valid client connection
let client_socket = match client_result.as_any().downcast_ref::<SocketBox>() {
Some(socket) => socket.clone(),
None => continue, // Skip invalid connections
};
// Add to active connections
active_connections.lock().unwrap().push(Box::new(client_socket.clone()));
// Handle client in separate thread (simulate nowait)
let routes_clone = Arc::clone(&routes);
let active_connections_clone = Arc::clone(&active_connections);
thread::spawn(move || {
Self::handle_client_request(client_socket, routes_clone);
// Remove from active connections when done
// Note: This is a simplified cleanup - real implementation would need proper tracking
let mut connections = active_connections_clone.lock().unwrap();
connections.retain(|conn| {
// Simple cleanup - remove all connections for now
// Real implementation would track by ID
false
});
});
}
Box::new(BoolBox::new(true))
} else {
Box::new(BoolBox::new(false))
}
}
/// サーバー停止
pub fn stop(&self) -> Box<dyn NyashBox> {
*self.running.lock().unwrap() = false;
// Close all active connections
let mut connections = self.active_connections.lock().unwrap();
for connection in connections.iter() {
if let Some(socket) = connection.as_any().downcast_ref::<SocketBox>() {
let _ = socket.close();
}
}
connections.clear();
// Close server socket
if let Some(ref socket) = *self.socket.lock().unwrap() {
let _ = socket.close();
}
println!("🛑 HTTP Server stopped");
Box::new(BoolBox::new(true))
}
/// ルート・ハンドラー登録
pub fn route(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("ANY {}", path_str);
self.routes.lock().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// GET ルート登録
pub fn get(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("GET {}", path_str);
self.routes.lock().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// POST ルート登録
pub fn post(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("POST {}", path_str);
self.routes.lock().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// PUT ルート登録
pub fn put(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("PUT {}", path_str);
self.routes.lock().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// DELETE ルート登録
pub fn delete(&self, path: Box<dyn NyashBox>, handler: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
let route_key = format!("DELETE {}", path_str);
self.routes.lock().unwrap().insert(route_key, handler);
Box::new(BoolBox::new(true))
}
/// 静的ファイル配信パス設定
pub fn set_static_path(&self, path: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let path_str = path.to_string_box().value;
*self.static_path.lock().unwrap() = Some(path_str);
Box::new(BoolBox::new(true))
}
/// リクエストタイムアウト設定
pub fn set_timeout(&self, seconds: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
let timeout_val = seconds.to_string_box().value.parse::<u64>().unwrap_or(30);
*self.timeout_seconds.lock().unwrap() = timeout_val;
Box::new(BoolBox::new(true))
}
/// クライアントリクエスト処理(内部メソッド)
fn handle_client_request(
client_socket: SocketBox,
routes: Arc<Mutex<HashMap<String, Box<dyn NyashBox>>>>
) {
// Read HTTP request
let raw_request = client_socket.read_http_request();
let request_str = raw_request.to_string_box().value;
if request_str.trim().is_empty() {
let _ = client_socket.close();
return;
}
// Parse HTTP request
let request = HTTPRequestBox::parse(raw_request);
let method = request.get_method().to_string_box().value;
let path = request.get_path().to_string_box().value;
println!("📬 {} {}", method, path);
// Find matching route
let routes_guard = routes.lock().unwrap();
let route_key = format!("{} {}", method, path);
let fallback_key = format!("ANY {}", path);
let response = if let Some(_handler) = routes_guard.get(&route_key) {
// Found specific method route
// TODO: Actual handler invocation would need method calling infrastructure
HTTPResponseBox::create_json_response(
Box::new(StringBox::new(r#"{"message": "Route found", "method": ""#.to_string() + &method + r#""}"#))
)
} else if let Some(_handler) = routes_guard.get(&fallback_key) {
// Found generic route
HTTPResponseBox::create_json_response(
Box::new(StringBox::new(r#"{"message": "Generic route found"}"#))
)
} else {
// No route found - 404
HTTPResponseBox::create_404_response()
};
drop(routes_guard);
// Send response
let response_str = response.to_http_string();
let _ = client_socket.write(response_str);
let _ = client_socket.close();
}
/// アクティブ接続数取得
pub fn get_active_connections(&self) -> Box<dyn NyashBox> {
let connections = self.active_connections.lock().unwrap();
Box::new(IntegerBox::new(connections.len() as i64))
}
/// サーバー状態取得
pub fn is_running(&self) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(*self.running.lock().unwrap()))
}
}
impl NyashBox for HTTPServerBox {
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn to_string_box(&self) -> StringBox {
let running = *self.running.lock().unwrap();
let routes_count = self.routes.lock().unwrap().len();
let connections_count = self.active_connections.lock().unwrap().len();
StringBox::new(format!(
"HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
self.base.id, running, routes_count, connections_count
))
}
fn type_name(&self) -> &'static str {
"HTTPServerBox"
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(other_server) = other.as_any().downcast_ref::<HTTPServerBox>() {
BoolBox::new(self.base.id == other_server.base.id)
} else {
BoolBox::new(false)
}
}
}
impl BoxCore for HTTPServerBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
self.base.parent_type_id
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let running = *self.running.lock().unwrap();
let routes_count = self.routes.lock().unwrap().len();
let connections_count = self.active_connections.lock().unwrap().len();
write!(f, "HTTPServer(id: {}, running: {}, routes: {}, connections: {})",
self.base.id, running, routes_count, connections_count)
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl std::fmt::Display for HTTPServerBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
// Auto-cleanup implementation for proper resource management
impl Drop for HTTPServerBox {
fn drop(&mut self) {
// Ensure server is stopped and resources are cleaned up
let _ = self.stop();
}
}