/*! 🌐 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; use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox}; use std::any::Any; use std::sync::RwLock; use std::collections::HashMap; use std::thread; /// HTTP サーバーを提供するBox #[derive(Debug)] pub struct HTTPServerBox { base: BoxBase, socket: RwLock>, routes: RwLock>>, middleware: RwLock>>, running: RwLock, static_path: RwLock>, timeout_seconds: RwLock, active_connections: RwLock>>, } impl Clone for HTTPServerBox { fn clone(&self) -> Self { // State-preserving clone implementation following PR #87 pattern let socket_guard = self.socket.read().unwrap(); let socket_val = socket_guard.as_ref().map(|s| s.clone()); let routes_guard = self.routes.read().unwrap(); let routes_val: HashMap> = routes_guard.iter() .map(|(k, v)| (k.clone(), v.clone_box())) .collect(); let middleware_guard = self.middleware.read().unwrap(); let middleware_val: Vec> = middleware_guard.iter() .map(|item| item.clone_box()) .collect(); let running_val = *self.running.read().unwrap(); let static_path_val = self.static_path.read().unwrap().clone(); let timeout_val = *self.timeout_seconds.read().unwrap(); let connections_guard = self.active_connections.read().unwrap(); let connections_val: Vec> = connections_guard.iter() .map(|item| item.clone_box()) .collect(); Self { base: BoxBase::new(), // New unique ID for clone socket: RwLock::new(socket_val), routes: RwLock::new(routes_val), middleware: RwLock::new(middleware_val), running: RwLock::new(running_val), static_path: RwLock::new(static_path_val), timeout_seconds: RwLock::new(timeout_val), active_connections: RwLock::new(connections_val), } } } impl HTTPServerBox { pub fn new() -> Self { Self { base: BoxBase::new(), socket: RwLock::new(None), routes: RwLock::new(HashMap::new()), middleware: RwLock::new(Vec::new()), running: RwLock::new(false), static_path: RwLock::new(None), timeout_seconds: RwLock::new(30), active_connections: RwLock::new(Vec::new()), } } /// サーバーアドレスにバインド pub fn bind(&self, address: Box, port: Box) -> Box { let socket = SocketBox::new(); let bind_result = socket.bind(address, port); if bind_result.to_string_box().value == "true" { match self.socket.write() { Ok(mut socket_guard) => { *socket_guard = Some(socket); Box::new(BoolBox::new(true)) }, Err(_) => { Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())) } } } else { Box::new(BoolBox::new(false)) } } /// 接続待機開始 pub fn listen(&self, _backlog: Box) -> Box { let socket_guard = match self.socket.read() { Ok(guard) => guard, Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())), }; if let Some(ref _socket) = *socket_guard { // For HTTPServerBox, if we have a socket stored, it means bind() was successful // and the socket should be in listening state. TcpListener::bind already puts // the socket in listening state, so we just need to verify it's working. // Try to access the stored listener directly (this is a simplified check) // In a real implementation, we'd store the listener state separately Box::new(BoolBox::new(true)) } else { Box::new(BoolBox::new(false)) } } /// HTTP サーバー開始(メインループ) pub fn start(&self) -> Box { // Set running state match self.running.write() { Ok(mut running) => *running = true, Err(_) => return Box::new(StringBox::new("Error: Failed to set running state".to_string())), }; let socket_guard = match self.socket.read() { Ok(guard) => guard, Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())), }; 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 - need to handle RwLock references carefully for threading loop { // Check if server should stop let should_continue = match self.running.read() { Ok(running_guard) => *running_guard, Err(_) => break, // Exit loop if we can't check running state }; if !should_continue { 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::() { Some(socket) => socket.clone(), None => continue, // Skip invalid connections }; // Add to active connections (with error handling) if let Ok(mut connections) = self.active_connections.write() { connections.push(Box::new(client_socket.clone())); } // Handle client in separate thread (simulate nowait) // For RwLock pattern, we need to pass the data needed for the thread let routes_snapshot = match self.routes.read() { Ok(routes_guard) => { let routes_clone: HashMap> = routes_guard.iter() .map(|(k, v)| (k.clone(), v.clone_box())) .collect(); routes_clone }, Err(_) => continue, // Skip this connection if we can't read routes }; thread::spawn(move || { Self::handle_client_request_with_routes(client_socket, routes_snapshot); // Note: Connection cleanup is handled separately to avoid complex lifetime issues }); } Box::new(BoolBox::new(true)) } else { Box::new(BoolBox::new(false)) } } /// サーバー停止 pub fn stop(&self) -> Box { *self.running.write().unwrap() = false; // Close all active connections let mut connections = self.active_connections.write().unwrap(); for connection in connections.iter() { if let Some(socket) = connection.as_any().downcast_ref::() { let _ = socket.close(); } } connections.clear(); // Close server socket if let Some(ref socket) = *self.socket.read().unwrap() { let _ = socket.close(); } println!("🛑 HTTP Server stopped"); Box::new(BoolBox::new(true)) } /// ルート・ハンドラー登録 pub fn route(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("ANY {}", path_str); self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } /// GET ルート登録 pub fn get(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("GET {}", path_str); self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } /// POST ルート登録 pub fn post(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("POST {}", path_str); self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } /// PUT ルート登録 pub fn put(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("PUT {}", path_str); self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } /// DELETE ルート登録 pub fn delete(&self, path: Box, handler: Box) -> Box { let path_str = path.to_string_box().value; let route_key = format!("DELETE {}", path_str); self.routes.write().unwrap().insert(route_key, handler); Box::new(BoolBox::new(true)) } /// 静的ファイル配信パス設定 pub fn set_static_path(&self, path: Box) -> Box { let path_str = path.to_string_box().value; *self.static_path.write().unwrap() = Some(path_str); Box::new(BoolBox::new(true)) } /// リクエストタイムアウト設定 pub fn set_timeout(&self, seconds: Box) -> Box { let timeout_val = seconds.to_string_box().value.parse::().unwrap_or(30); *self.timeout_seconds.write().unwrap() = timeout_val; Box::new(BoolBox::new(true)) } /// クライアントリクエスト処理(内部メソッド) fn handle_client_request_with_routes( client_socket: SocketBox, routes: HashMap> ) { // 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 route_key = format!("{} {}", method, path); let fallback_key = format!("ANY {}", path); let response = if let Some(_handler) = routes.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.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() }; // 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 { let connections = self.active_connections.read().unwrap(); Box::new(IntegerBox::new(connections.len() as i64)) } /// サーバー状態取得 pub fn is_running(&self) -> Box { Box::new(BoolBox::new(*self.running.read().unwrap())) } } impl NyashBox for HTTPServerBox { fn clone_box(&self) -> Box { Box::new(self.clone()) } /// 仮実装: clone_boxと同じ(後で修正) fn share_box(&self) -> Box { self.clone_box() } fn to_string_box(&self) -> StringBox { let running = *self.running.read().unwrap(); let routes_count = self.routes.read().unwrap().len(); let connections_count = self.active_connections.read().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::() { 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 { self.base.parent_type_id } fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let running = *self.running.read().unwrap(); let routes_count = self.routes.read().unwrap().len(); let connections_count = self.active_connections.read().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(); } }