Delete src/server/http.rs
This commit is contained in:
parent
057d2c55c7
commit
b45d592a2d
@ -1,376 +0,0 @@
|
|||||||
// 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::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<String>,
|
|
||||||
pub denied_ips: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Body>,
|
|
||||||
db: Arc<Database>,
|
|
||||||
static_config: StaticFilesConfig,
|
|
||||||
acl_config: AclConfig,
|
|
||||||
) -> Result<Response<Body>> {
|
|
||||||
// Проверка ACL, если включена
|
|
||||||
if acl_config.enabled {
|
|
||||||
if let Some(remote_addr) = req.extensions().get::<std::net::SocketAddr>() {
|
|
||||||
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();
|
|
||||||
|
|
||||||
println!("HTTP Request: {} {}", req.method(), 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 == "/" {
|
|
||||||
// ВОЗВРАЩАЕМ ПРОСТОЙ HTML ДЛЯ КОРНЕВОГО ПУТИ
|
|
||||||
Ok(Response::builder()
|
|
||||||
.header("Content-Type", "text/html; charset=utf-8")
|
|
||||||
.body(Body::from(r#"
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Futriix Database Server</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; margin: 40px; }
|
|
||||||
h1 { color: #00bfff; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Futriix Database Server</h1>
|
|
||||||
<p>Server is running successfully!</p>
|
|
||||||
<p>Try accessing <a href="/index.html">/index.html</a> for the main interface.</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"#))
|
|
||||||
.unwrap())
|
|
||||||
}
|
|
||||||
// 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<Body>,
|
|
||||||
_db: Arc<Database>,
|
|
||||||
) -> Result<Response<Body>> {
|
|
||||||
// TODO: Реализовать wait-free обработку CRUD операций через HTTP
|
|
||||||
Ok(Response::builder()
|
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.body(Body::from(r#"{"status": "ok", "message": "Futriix Server is running"}"#))
|
|
||||||
.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wait-free обслуживание статических файлов
|
|
||||||
async fn handle_static_file(
|
|
||||||
path: &str,
|
|
||||||
config: StaticFilesConfig,
|
|
||||||
) -> Result<Response<Body>> {
|
|
||||||
// Убираем начальный слеш из пути
|
|
||||||
let clean_path = path.trim_start_matches('/');
|
|
||||||
|
|
||||||
// Если путь пустой или корневой, используем index.html
|
|
||||||
let file_path = if clean_path.is_empty() || clean_path == "/" {
|
|
||||||
format!("{}/index.html", config.directory)
|
|
||||||
} else {
|
|
||||||
format!("{}/{}", config.directory, clean_path)
|
|
||||||
};
|
|
||||||
|
|
||||||
// ДОБАВЛЯЕМ ДЕБАГ-ЛОГИРОВАНИЕ
|
|
||||||
println!("Trying to serve static file: {}", file_path);
|
|
||||||
|
|
||||||
match File::open(&file_path).await {
|
|
||||||
Ok(mut file) => {
|
|
||||||
let mut contents = Vec::new();
|
|
||||||
if let Err(e) = file.read_to_end(&mut contents).await {
|
|
||||||
eprintln!("Failed to read file {}: {}", file_path, e);
|
|
||||||
return Ok(Response::builder()
|
|
||||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
.body(Body::from("Internal server error"))
|
|
||||||
.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
let content_type = get_content_type(&file_path);
|
|
||||||
|
|
||||||
println!("Successfully served static file: {} ({} bytes)", file_path, contents.len());
|
|
||||||
|
|
||||||
Ok(Response::builder()
|
|
||||||
.header("Content-Type", content_type)
|
|
||||||
.body(Body::from(contents))
|
|
||||||
.unwrap())
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("File not found: {} (error: {})", file_path, e);
|
|
||||||
|
|
||||||
// ДОБАВЛЯЕМ ПРОСТОЙ HTML ДЛЯ ТЕСТИРОВАНИЯ, ЕСЛИ ФАЙЛ НЕ НАЙДЕН
|
|
||||||
if clean_path == "index.html" {
|
|
||||||
let fallback_html = r#"
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Futriix Database Server</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: Arial, sans-serif; margin: 40px; }
|
|
||||||
h1 { color: #00bfff; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Futriix Database Server</h1>
|
|
||||||
<p>Welcome to Futriix Database Server!</p>
|
|
||||||
<p>Static file serving is working correctly.</p>
|
|
||||||
<p>Current time: PLACEHOLDER_TIME</p>
|
|
||||||
<p>Requested path: PLACEHOLDER_PATH</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"#.replace("PLACEHOLDER_TIME", &chrono::Local::now().to_rfc2822())
|
|
||||||
.replace("PLACEHOLDER_PATH", path);
|
|
||||||
|
|
||||||
return Ok(Response::builder()
|
|
||||||
.header("Content-Type", "text/html; charset=utf-8")
|
|
||||||
.body(Body::from(fallback_html))
|
|
||||||
.unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
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; charset=utf-8"
|
|
||||||
} else if file_path.ends_with(".css") {
|
|
||||||
"text/css; charset=utf-8"
|
|
||||||
} else if file_path.ends_with(".js") {
|
|
||||||
"application/javascript; charset=utf-8"
|
|
||||||
} 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; charset=utf-8"
|
|
||||||
} else if file_path.ends_with(".ico") {
|
|
||||||
"image/x-icon"
|
|
||||||
} else if file_path.ends_with(".svg") {
|
|
||||||
"image/svg+xml"
|
|
||||||
} else {
|
|
||||||
"text/plain; charset=utf-8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Запуск HTTP сервера с wait-free архитектурой
|
|
||||||
pub async fn start_http_server(
|
|
||||||
addr: &str,
|
|
||||||
db: Arc<Database>,
|
|
||||||
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::FutriixError::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| {
|
|
||||||
let db = db.clone();
|
|
||||||
let static_config = static_config.clone();
|
|
||||||
let acl_config = acl_config.clone();
|
|
||||||
async move {
|
|
||||||
handle_request(req, db, static_config, acl_config).await
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ИЗМЕНЕНИЕ: Убрано многоточие
|
|
||||||
println!("HTTP server starting on {}", addr);
|
|
||||||
|
|
||||||
// ЗАПУСКАЕМ СЕРВЕР И БЛОКИРУЕМСЯ НА ЕГО ВЫПОЛНЕНИИ
|
|
||||||
// Это гарантирует, что сервер продолжит работать
|
|
||||||
let server = Server::bind(&addr_parsed).serve(make_svc);
|
|
||||||
|
|
||||||
if let Err(e) = server.await {
|
|
||||||
eprintln!("HTTP server error: {}", e);
|
|
||||||
return Err(crate::common::FutriixError::HttpError(e.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Запуск HTTPS сервера с wait-free архитектурой
|
|
||||||
pub async fn start_https_server(
|
|
||||||
addr: &str,
|
|
||||||
db: Arc<Database>,
|
|
||||||
static_config: StaticFilesConfig,
|
|
||||||
tls_config: TlsConfig,
|
|
||||||
acl_config: AclConfig,
|
|
||||||
) -> Result<()> {
|
|
||||||
use tokio::net::TcpListener;
|
|
||||||
|
|
||||||
if !tls_config.enabled {
|
|
||||||
println!("HTTPS disabled: TLS not enabled");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// ПРОСТОЙ ВАРИАНТ БЕЗ TLS ДЛЯ ТЕСТИРОВАНИЯ
|
|
||||||
// В реальном коде здесь должна быть TLS конфигурация
|
|
||||||
println!("HTTPS server would start on {} (TLS configuration needed)", addr);
|
|
||||||
|
|
||||||
// ЗАПУСКАЕМ ОБЫЧНЫЙ HTTP СЕРВЕР НА HTTPS ПОРТУ ДЛЯ ТЕСТИРОВАНИЯ
|
|
||||||
// Но используем тот же порт, чтобы не путать
|
|
||||||
let http_config = HttpConfig {
|
|
||||||
enabled: true,
|
|
||||||
port: 8443, // Используем HTTPS порт для тестирования
|
|
||||||
http2_enabled: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// ИСПРАВЛЕНИЕ ОШИБКИ: создаем owned копии всех данных для использования в async move
|
|
||||||
let owned_addr = addr.to_string();
|
|
||||||
let owned_db = db.clone();
|
|
||||||
let owned_static_config = static_config.clone();
|
|
||||||
let owned_acl_config = acl_config.clone();
|
|
||||||
|
|
||||||
// Запускаем обычный HTTP сервер на HTTPS порту для тестирования
|
|
||||||
// Это временное решение до настройки TLS
|
|
||||||
let server_future = async move {
|
|
||||||
start_http_server(&owned_addr, owned_db, owned_static_config, http_config, owned_acl_config).await
|
|
||||||
};
|
|
||||||
|
|
||||||
// Запускаем сервер в отдельной задаче
|
|
||||||
tokio::spawn(async move {
|
|
||||||
if let Err(e) = server_future.await {
|
|
||||||
eprintln!("HTTPS (HTTP fallback) 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("futriix.log")
|
|
||||||
{
|
|
||||||
// ИСПРАВЛЕНИЕ: Используем системное время с миллисекундами
|
|
||||||
let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string();
|
|
||||||
let log_message = format!("[{}] {}\n", timestamp, message);
|
|
||||||
let _ = file.write_all(log_message.as_bytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user