// src/server/http.rs //! HTTP/HTTPS сервер с wait-free обработкой запросов #![allow(dead_code)] #![allow(unused_variables)] use std::sync::Arc; use hyper::{Body, Request, Response, Server, StatusCode}; use hyper::service::{make_service_fn, service_fn}; use tokio::fs::File; use tokio::io::AsyncReadExt; use crate::common::error::Result; use crate::server::database::Database; /// Конфигурация статических файлов #[derive(Clone)] pub struct StaticFilesConfig { pub enabled: bool, pub directory: String, } impl Default for StaticFilesConfig { fn default() -> Self { Self { enabled: true, directory: "static".to_string(), } } } /// Конфигурация TLS #[derive(Clone)] pub struct TlsConfig { pub enabled: bool, pub cert_path: String, pub key_path: String, } impl Default for TlsConfig { fn default() -> Self { Self { enabled: true, cert_path: "certs/cert.pem".to_string(), key_path: "certs/key.pem".to_string(), } } } /// Конфигурация HTTP сервера #[derive(Clone)] pub struct HttpConfig { pub enabled: bool, pub port: u16, pub http2_enabled: bool, } /// Конфигурация ACL #[derive(Clone)] pub struct AclConfig { pub enabled: bool, pub allowed_ips: Vec, pub denied_ips: Vec, } impl Default for AclConfig { fn default() -> Self { Self { enabled: false, allowed_ips: vec!["127.0.0.1".to_string(), "::1".to_string()], denied_ips: vec![], } } } /// Wait-free обработчик HTTP запросов с поддержкой ACL async fn handle_request( req: Request, db: Arc, static_config: StaticFilesConfig, acl_config: AclConfig, ) -> Result> { // Проверка ACL, если включена if acl_config.enabled { if let Some(remote_addr) = req.extensions().get::() { let ip = remote_addr.ip().to_string(); // Проверка запрещенных IP if acl_config.denied_ips.contains(&ip) { return Ok(Response::builder() .status(StatusCode::FORBIDDEN) .body(Body::from("Access denied")) .unwrap()); } // Проверка разрешенных IP (если список не пустой) if !acl_config.allowed_ips.is_empty() && !acl_config.allowed_ips.contains(&ip) { return Ok(Response::builder() .status(StatusCode::FORBIDDEN) .body(Body::from("Access denied")) .unwrap()); } } } let path = req.uri().path(); // Обработка API запросов if path.starts_with("/api/") { handle_api_request(req, db).await } // Обслуживание статических файлов else if static_config.enabled { handle_static_file(path, static_config).await } // Корневой путь else if path == "/" { Ok(Response::new(Body::from("Falcot Database Server - HTTP API"))) } // 404 для остальных запросов else { Ok(Response::builder() .status(StatusCode::NOT_FOUND) .body(Body::from("Not Found")) .unwrap()) } } /// Wait-free обработка API запросов async fn handle_api_request( _req: Request, _db: Arc, ) -> Result> { // TODO: Реализовать wait-free обработку CRUD операций через HTTP Ok(Response::new(Body::from(r#"{"status": "ok", "message": "Falcot Server is running"}"#))) } /// Wait-free обслуживание статических файлов async fn handle_static_file( path: &str, config: StaticFilesConfig, ) -> Result> { let file_path = if path == "/" { format!("{}/index.html", config.directory) } else { format!("{}{}", config.directory, path) }; match File::open(&file_path).await { Ok(mut file) => { let mut contents = Vec::new(); file.read_to_end(&mut contents).await .map_err(|e: std::io::Error| crate::common::error::FalcotError::HttpError(e.to_string()))?; let content_type = get_content_type(&file_path); Ok(Response::builder() .header("Content-Type", content_type) .body(Body::from(contents)) .unwrap()) } Err(_) => { Ok(Response::builder() .status(StatusCode::NOT_FOUND) .body(Body::from("File not found")) .unwrap()) } } } /// Определение Content-Type по расширению файла fn get_content_type(file_path: &str) -> &'static str { if file_path.ends_with(".html") { "text/html" } else if file_path.ends_with(".css") { "text/css" } else if file_path.ends_with(".js") { "application/javascript" } else if file_path.ends_with(".png") { "image/png" } else if file_path.ends_with(".jpg") || file_path.ends_with(".jpeg") { "image/jpeg" } else if file_path.ends_with(".json") { "application/json" } else if file_path.ends_with(".mp3") { "audio/mpeg" } else if file_path.ends_with(".mp4") { "video/mp4" } else { "text/plain" } } /// Запуск HTTP сервера с wait-free архитектурой pub async fn start_http_server( addr: &str, db: Arc, static_config: StaticFilesConfig, http_config: HttpConfig, acl_config: AclConfig, ) -> Result<()> { let addr_parsed: std::net::SocketAddr = addr.parse() .map_err(|e: std::net::AddrParseError| crate::common::error::FalcotError::HttpError(e.to_string()))?; let db_clone = db.clone(); let static_clone = static_config.clone(); let acl_clone = acl_config.clone(); // Создание wait-free сервиса let make_svc = make_service_fn(move |_conn| { let db = db_clone.clone(); let static_config = static_clone.clone(); let acl_config = acl_clone.clone(); async move { Ok::<_, hyper::Error>(service_fn(move |req| { handle_request(req, db.clone(), static_config.clone(), acl_config.clone()) })) } }); // Запуск сервера let builder = Server::bind(&addr_parsed); let server = if http_config.http2_enabled { // Включение HTTP/2 для HTTP сервера builder.http2_only(true).serve(make_svc) } else { // Использование HTTP/1.1 builder.serve(make_svc) }; // Запускаем сервер синхронно server.await .map_err(|e: hyper::Error| crate::common::error::FalcotError::HttpError(e.to_string()))?; Ok(()) } /// Запуск HTTPS сервера с wait-free архитектурой // Убираем лишний параметр http_config из сигнатуры функции pub async fn start_https_server( addr: &str, db: Arc, static_config: StaticFilesConfig, tls_config: TlsConfig, acl_config: AclConfig, ) -> Result<()> { use tokio::net::TcpListener; use tokio_rustls::TlsAcceptor; use rustls::{Certificate, PrivateKey, ServerConfig}; use rustls_pemfile::{certs, pkcs8_private_keys}; use std::fs::File; use std::io::BufReader; if !tls_config.enabled { println!("HTTPS disabled in configuration"); return Ok(()); } // Загрузка TLS сертификата и ключа let cert_file = File::open(&tls_config.cert_path) .map_err(|e| crate::common::error::FalcotError::HttpError(format!("Failed to open certificate: {}", e)))?; let key_file = File::open(&tls_config.key_path) .map_err(|e| crate::common::error::FalcotError::HttpError(format!("Failed to open key: {}", e)))?; // Загрузка сертификатов let cert_chain: Vec = certs(&mut BufReader::new(cert_file)) .map_err(|e| crate::common::error::FalcotError::HttpError(format!("Failed to parse certificate: {}", e)))? .into_iter() .map(Certificate) .collect(); // Загрузка приватного ключа let mut keys: Vec = pkcs8_private_keys(&mut BufReader::new(key_file)) .map_err(|e| crate::common::error::FalcotError::HttpError(format!("Failed to parse key: {}", e)))? .into_iter() .map(PrivateKey) .collect(); if keys.is_empty() { return Err(crate::common::error::FalcotError::HttpError("No private keys found".to_string())); } // Создание конфигурации сервера TLS let server_config = ServerConfig::builder() .with_safe_defaults() .with_no_client_auth() .with_single_cert(cert_chain, keys.remove(0)) .map_err(|e| crate::common::error::FalcotError::HttpError(format!("Failed to create server config: {}", e)))?; let addr_parsed: std::net::SocketAddr = addr.parse() .map_err(|e: std::net::AddrParseError| crate::common::error::FalcotError::HttpError(e.to_string()))?; let db_clone = db.clone(); let static_clone = static_config.clone(); let acl_clone = acl_config.clone(); // Создание TLS акцептора let tls_acceptor = TlsAcceptor::from(Arc::new(server_config)); // Создание TCP listener let tcp_listener = TcpListener::bind(&addr_parsed).await .map_err(|e| crate::common::error::FalcotError::HttpError(format!("Failed to bind to {}: {}", addr, e)))?; println!("HTTPS server starting on {}...", addr); // Принимаем и обрабатываем соединения while let Ok((tcp_stream, _)) = tcp_listener.accept().await { let tls_acceptor = tls_acceptor.clone(); let db = db_clone.clone(); let static_config = static_clone.clone(); let acl_config = acl_clone.clone(); tokio::spawn(async move { // Принимаем TLS соединение let tls_stream = match tls_acceptor.accept(tcp_stream).await { Ok(stream) => stream, Err(e) => { eprintln!("TLS handshake failed: {}", e); return; } }; // Создаем сервис для этого соединения let service = service_fn(move |req| { handle_request(req, db.clone(), static_config.clone(), acl_config.clone()) }); // Обрабатываем HTTP запросы поверх TLS if let Err(e) = hyper::server::conn::Http::new() .serve_connection(tls_stream, service) .await { eprintln!("HTTP server error: {}", e); } }); } Ok(()) } // Вспомогательная функция для логирования fn log_to_file(message: &str) { use std::fs::OpenOptions; use std::io::Write; if let Ok(mut file) = OpenOptions::new() .create(true) .append(true) .open("falcot.log") { let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"); let log_message = format!("[{}] {}\n", timestamp, message); let _ = file.write_all(log_message.as_bytes()); } }