2025-08-14 06:10:52 +00:00
|
|
|
|
/*! 🌐 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};
|
2025-08-15 03:08:09 +00:00
|
|
|
|
use crate::boxes::SocketBox;
|
2025-08-14 06:10:52 +00:00
|
|
|
|
use crate::boxes::http_message_box::{HTTPRequestBox, HTTPResponseBox};
|
|
|
|
|
|
use std::any::Any;
|
2025-08-15 01:00:21 +00:00
|
|
|
|
use std::sync::RwLock;
|
2025-08-14 06:10:52 +00:00
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
use std::thread;
|
|
|
|
|
|
|
|
|
|
|
|
/// HTTP サーバーを提供するBox
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
pub struct HTTPServerBox {
|
|
|
|
|
|
base: BoxBase,
|
2025-08-15 01:00:21 +00:00
|
|
|
|
socket: RwLock<Option<SocketBox>>,
|
|
|
|
|
|
routes: RwLock<HashMap<String, Box<dyn NyashBox>>>,
|
|
|
|
|
|
middleware: RwLock<Vec<Box<dyn NyashBox>>>,
|
|
|
|
|
|
running: RwLock<bool>,
|
|
|
|
|
|
static_path: RwLock<Option<String>>,
|
|
|
|
|
|
timeout_seconds: RwLock<u64>,
|
|
|
|
|
|
active_connections: RwLock<Vec<Box<dyn NyashBox>>>,
|
2025-08-14 06:10:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl Clone for HTTPServerBox {
|
|
|
|
|
|
fn clone(&self) -> Self {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
// State-preserving clone implementation following PR #87 pattern
|
2025-08-15 03:08:09 +00:00
|
|
|
|
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<String, Box<dyn NyashBox>> = routes_guard.iter()
|
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.clone_box()))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
|
|
let middleware_guard = self.middleware.read().unwrap();
|
|
|
|
|
|
let middleware_val: Vec<Box<dyn NyashBox>> = middleware_guard.iter()
|
|
|
|
|
|
.map(|item| item.clone_box())
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
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();
|
2025-08-15 03:08:09 +00:00
|
|
|
|
|
|
|
|
|
|
let connections_guard = self.active_connections.read().unwrap();
|
|
|
|
|
|
let connections_val: Vec<Box<dyn NyashBox>> = connections_guard.iter()
|
|
|
|
|
|
.map(|item| item.clone_box())
|
|
|
|
|
|
.collect();
|
2025-08-15 01:00:21 +00:00
|
|
|
|
|
2025-08-14 06:10:52 +00:00
|
|
|
|
Self {
|
|
|
|
|
|
base: BoxBase::new(), // New unique ID for clone
|
2025-08-15 01:00:21 +00:00
|
|
|
|
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),
|
2025-08-14 06:10:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl HTTPServerBox {
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
|
Self {
|
|
|
|
|
|
base: BoxBase::new(),
|
2025-08-15 01:00:21 +00:00
|
|
|
|
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()),
|
2025-08-14 06:10:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// サーバーアドレスにバインド
|
|
|
|
|
|
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" {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
match self.socket.write() {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
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()))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-14 06:10:52 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
Box::new(BoolBox::new(false))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 接続待機開始
|
|
|
|
|
|
pub fn listen(&self, backlog: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let socket_guard = match self.socket.read() {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
Ok(guard) => guard,
|
|
|
|
|
|
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-14 06:10:52 +00:00
|
|
|
|
if let Some(ref socket) = *socket_guard {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
// 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))
|
2025-08-14 06:10:52 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
Box::new(BoolBox::new(false))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// HTTP サーバー開始(メインループ)
|
|
|
|
|
|
pub fn start(&self) -> Box<dyn NyashBox> {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
// Set running state
|
2025-08-15 01:00:21 +00:00
|
|
|
|
match self.running.write() {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
Ok(mut running) => *running = true,
|
|
|
|
|
|
Err(_) => return Box::new(StringBox::new("Error: Failed to set running state".to_string())),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let socket_guard = match self.socket.read() {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
Ok(guard) => guard,
|
|
|
|
|
|
Err(_) => return Box::new(StringBox::new("Error: Failed to acquire socket lock".to_string())),
|
|
|
|
|
|
};
|
2025-08-14 06:10:52 +00:00
|
|
|
|
|
|
|
|
|
|
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...");
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
// Main server loop - need to handle RwLock references carefully for threading
|
2025-08-14 06:10:52 +00:00
|
|
|
|
loop {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
// Check if server should stop
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let should_continue = match self.running.read() {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
Ok(running_guard) => *running_guard,
|
|
|
|
|
|
Err(_) => break, // Exit loop if we can't check running state
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if !should_continue {
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-14 07:14:45 +00:00
|
|
|
|
// Add to active connections (with error handling)
|
2025-08-15 01:00:21 +00:00
|
|
|
|
if let Ok(mut connections) = self.active_connections.write() {
|
2025-08-14 07:14:45 +00:00
|
|
|
|
connections.push(Box::new(client_socket.clone()));
|
|
|
|
|
|
}
|
2025-08-14 06:10:52 +00:00
|
|
|
|
|
|
|
|
|
|
// Handle client in separate thread (simulate nowait)
|
2025-08-15 01:00:21 +00:00
|
|
|
|
// For RwLock pattern, we need to pass the data needed for the thread
|
|
|
|
|
|
let routes_snapshot = match self.routes.read() {
|
2025-08-15 03:08:09 +00:00
|
|
|
|
Ok(routes_guard) => {
|
|
|
|
|
|
let routes_clone: HashMap<String, Box<dyn NyashBox>> = routes_guard.iter()
|
|
|
|
|
|
.map(|(k, v)| (k.clone(), v.clone_box()))
|
|
|
|
|
|
.collect();
|
|
|
|
|
|
routes_clone
|
|
|
|
|
|
},
|
2025-08-15 01:00:21 +00:00
|
|
|
|
Err(_) => continue, // Skip this connection if we can't read routes
|
|
|
|
|
|
};
|
2025-08-14 06:10:52 +00:00
|
|
|
|
|
|
|
|
|
|
thread::spawn(move || {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
Self::handle_client_request_with_routes(client_socket, routes_snapshot);
|
|
|
|
|
|
// Note: Connection cleanup is handled separately to avoid complex lifetime issues
|
2025-08-14 06:10:52 +00:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Box::new(BoolBox::new(true))
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Box::new(BoolBox::new(false))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// サーバー停止
|
|
|
|
|
|
pub fn stop(&self) -> Box<dyn NyashBox> {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
*self.running.write().unwrap() = false;
|
2025-08-14 06:10:52 +00:00
|
|
|
|
|
|
|
|
|
|
// Close all active connections
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let mut connections = self.active_connections.write().unwrap();
|
2025-08-14 06:10:52 +00:00
|
|
|
|
for connection in connections.iter() {
|
|
|
|
|
|
if let Some(socket) = connection.as_any().downcast_ref::<SocketBox>() {
|
|
|
|
|
|
let _ = socket.close();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
connections.clear();
|
|
|
|
|
|
|
|
|
|
|
|
// Close server socket
|
2025-08-15 01:00:21 +00:00
|
|
|
|
if let Some(ref socket) = *self.socket.read().unwrap() {
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
self.routes.write().unwrap().insert(route_key, handler);
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
self.routes.write().unwrap().insert(route_key, handler);
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
self.routes.write().unwrap().insert(route_key, handler);
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
self.routes.write().unwrap().insert(route_key, handler);
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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);
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
self.routes.write().unwrap().insert(route_key, handler);
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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;
|
2025-08-15 01:00:21 +00:00
|
|
|
|
*self.static_path.write().unwrap() = Some(path_str);
|
2025-08-14 06:10:52 +00:00
|
|
|
|
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);
|
2025-08-15 01:00:21 +00:00
|
|
|
|
*self.timeout_seconds.write().unwrap() = timeout_val;
|
2025-08-14 06:10:52 +00:00
|
|
|
|
Box::new(BoolBox::new(true))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// クライアントリクエスト処理(内部メソッド)
|
2025-08-15 01:00:21 +00:00
|
|
|
|
fn handle_client_request_with_routes(
|
2025-08-14 06:10:52 +00:00
|
|
|
|
client_socket: SocketBox,
|
2025-08-15 01:00:21 +00:00
|
|
|
|
routes: HashMap<String, Box<dyn NyashBox>>
|
2025-08-14 06:10:52 +00:00
|
|
|
|
) {
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let response = if let Some(_handler) = routes.get(&route_key) {
|
2025-08-14 06:10:52 +00:00
|
|
|
|
// 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#""}"#))
|
|
|
|
|
|
)
|
2025-08-15 01:00:21 +00:00
|
|
|
|
} else if let Some(_handler) = routes.get(&fallback_key) {
|
2025-08-14 06:10:52 +00:00
|
|
|
|
// 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<dyn NyashBox> {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let connections = self.active_connections.read().unwrap();
|
2025-08-14 06:10:52 +00:00
|
|
|
|
Box::new(IntegerBox::new(connections.len() as i64))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// サーバー状態取得
|
|
|
|
|
|
pub fn is_running(&self) -> Box<dyn NyashBox> {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
Box::new(BoolBox::new(*self.running.read().unwrap()))
|
2025-08-14 06:10:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl NyashBox for HTTPServerBox {
|
|
|
|
|
|
fn clone_box(&self) -> Box<dyn NyashBox> {
|
|
|
|
|
|
Box::new(self.clone())
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn to_string_box(&self) -> StringBox {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let running = *self.running.read().unwrap();
|
|
|
|
|
|
let routes_count = self.routes.read().unwrap().len();
|
|
|
|
|
|
let connections_count = self.active_connections.read().unwrap().len();
|
2025-08-14 06:10:52 +00:00
|
|
|
|
|
|
|
|
|
|
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 {
|
2025-08-15 01:00:21 +00:00
|
|
|
|
let running = *self.running.read().unwrap();
|
|
|
|
|
|
let routes_count = self.routes.read().unwrap().len();
|
|
|
|
|
|
let connections_count = self.active_connections.read().unwrap().len();
|
2025-08-14 06:10:52 +00:00
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|