1use std::fs;
2use std::io::{BufRead, BufReader};
3use std::net::{TcpListener, TcpStream};
4use std::path::Path;
5use std::sync::Arc;
6use std::thread;
7use std::time::Duration;
8use std::io::Write;
9
10use crate::config::ServerConfig;
11use crate::http::{
12 HttpMethod, StatusCode,
13 send_get_response, send_head_response,
14 send_not_found_response,
15 send_method_not_allowed_response,
16 send_internal_server_error_response,
17 send_not_implemented_response,
18 send_http_version_not_supported_response,
19 send_forbidden_response,
20};
21use crate::utils::{get_content_type, Logger, StatsManager};
22
23pub struct WebServer {
25 config: ServerConfig,
26 logger: Arc<Logger>,
27 stats_manager: Arc<StatsManager>,
28}
29
30impl WebServer {
31 pub fn new(config: ServerConfig) -> Self {
41 let logger = match Logger::new(&config) {
42 Ok(logger) => Arc::new(logger),
43 Err(e) => {
44 eprintln!("Erreur lors de l'initialisation du fichier de log: {}. La journalisation des requêtes sera désactivée.", e);
45 Arc::new(Logger::new(&ServerConfig {
46 log_file: "/dev/null".to_string(),
47 ..config.clone()
48 }).unwrap_or_else(|_| panic!("Impossible de créer un logger fallback")))
49 }
50 };
51
52 let stats_manager = Arc::new(StatsManager::new());
53
54 Self { config, logger, stats_manager }
55 }
56
57 pub fn run(&self) {
59 let bind_address = self.config.bind_address();
60 let listener = match TcpListener::bind(&bind_address) {
61 Ok(listener) => listener,
62 Err(e) => {
63 let _ = self.logger.log_message(&format!("Erreur lors de la liaison à l'adresse {}: {}", bind_address, e));
64 eprintln!("Erreur lors de la liaison à l'adresse {}: {}", bind_address, e);
65 return;
66 }
67 };
68
69 let _ = self.logger.log_message(&format!("Serveur démarré sur {}", bind_address));
70 let _ = self.logger.log_message(&format!("Dossier public: {}", self.config.public_dir));
71
72 for stream in listener.incoming() {
73 match stream {
74 Ok(stream) => {
75 let config = self.config.clone();
76 let logger = Arc::clone(&self.logger);
77 let stats_manager = Arc::clone(&self.stats_manager);
78
79 thread::spawn(move || {
80 let server = WebServer { config, logger, stats_manager };
81 server.handle_connection(stream);
82 });
83 }
84 Err(e) => {
85 let _ = self.logger.log_message(&format!("Erreur de connexion: {}", e));
86 }
87 }
88 }
89 }
90
91 fn handle_connection(&self, mut stream: TcpStream) {
97 let peer_addr = match stream.peer_addr() {
98 Ok(addr) => addr,
99 Err(_) => {
100 let _ = self.logger.log_message("Impossible d'obtenir l'adresse du client");
101 return;
102 }
103 };
104
105 let keep_alive_timeout = Duration::from_secs(30);
106
107 loop {
108 if let Err(e) = stream.set_read_timeout(Some(keep_alive_timeout)) {
109 let _ = self.logger.log_message(&format!("Erreur lors de la configuration du timeout: {}", e));
110 break;
111 }
112
113 let buf_reader = BufReader::new(&stream);
114 let http_request: Result<Vec<String>, _> = buf_reader
115 .lines()
116 .take_while(|line| match line {
117 Ok(l) => !l.is_empty(),
118 Err(_) => false,
119 })
120 .collect();
121
122 let http_request = match http_request {
123 Ok(req) if !req.is_empty() => req,
124 _ => {
125 break;
126 }
127 };
128
129 let request_line = http_request.first().unwrap();
130 let mut parts = request_line.split_whitespace();
131 let method = parts.next().unwrap_or("");
132 let path = parts.next().unwrap_or("/");
133 let version = parts.next().unwrap_or("");
134
135 self.stats_manager.increment_request_count();
136
137 if !version.starts_with("HTTP/1.") {
138 let _ = self.logger.log_request(&peer_addr, method, path, StatusCode::HTTP_VERSION_NOT_SUPPORTED);
139 let _ = self.logger.log_message(&format!("Version HTTP non supportée: {}", version));
140
141 self.stats_manager.increment_error_count("505 HTTP Version Not Supported");
142
143 let _ = send_http_version_not_supported_response(&mut stream, "close");
144 break;
145 }
146
147 let connection_header = http_request
148 .iter()
149 .find(|line| line.to_lowercase().starts_with("connection:"))
150 .map(|line| line.split(':').nth(1).unwrap_or("").trim().to_lowercase())
151 .unwrap_or_else(|| "close".to_string());
152
153 if method == "GET" {
154 if path == "/stats" {
155 self.handle_stats_request(&peer_addr, &mut stream, &connection_header);
156 } else {
157 self.handle_get_request(&peer_addr, path, &mut stream, &connection_header);
158 }
159 } else if method == "HEAD" {
160 self.handle_head_request(&peer_addr, path, &mut stream, &connection_header);
161 } else if HttpMethod::from_str(method).is_some() {
162 let _ = self.logger.log_request(&peer_addr, method, path, StatusCode::NOT_IMPLEMENTED);
163 let _ = self.logger.log_message(&format!("Méthode non implémentée: {}", method));
164
165 self.stats_manager.increment_error_count("501 Not Implemented");
166
167 let _ = send_not_implemented_response(&mut stream, &connection_header);
168 } else {
169 let _ = self.logger.log_request(&peer_addr, method, path, StatusCode::METHOD_NOT_ALLOWED);
170 let _ = self.logger.log_message(&format!("Méthode non autorisée: {}", method));
171
172 self.stats_manager.increment_error_count("405 Method Not Allowed");
173
174 let _ = send_method_not_allowed_response(&mut stream, &connection_header);
175 }
176
177 if connection_header != "keep-alive" {
178 break;
179 }
180
181 }
182
183 let _ = self.logger.log_message(&format!("Connexion fermée pour {}", peer_addr));
184 }
185
186 fn handle_stats_request(&self, peer_addr: &std::net::SocketAddr, stream: &mut TcpStream, connection: &str) {
194 match self.stats_manager.to_json() {
195 Ok(json) => {
196 let _ = self.logger.log_request(peer_addr, "GET", "/stats", StatusCode::OK);
197 let _ = send_get_response(stream, StatusCode::OK, "application/json", json, connection);
198 }
199 Err(e) => {
200 let _ = self.logger.log_request(peer_addr, "GET", "/stats", StatusCode::INTERNAL_SERVER_ERROR);
201 let _ = self.logger.log_message(&format!("Erreur lors de la génération des statistiques: {}", e));
202
203 self.stats_manager.increment_error_count("500 Internal Server Error");
204
205 let _ = send_internal_server_error_response(stream, connection);
206 }
207 }
208 }
209
210 fn handle_get_request(&self, peer_addr: &std::net::SocketAddr, path: &str, stream: &mut TcpStream, connection: &str) {
219 if path.contains("..") {
220 let _ = self.logger.log_request(peer_addr, "GET", path, StatusCode::FORBIDDEN);
221 let _ = self.logger.log_message(&format!("Tentative d'accès non autorisé: {}", path));
222
223 self.stats_manager.increment_error_count("403 Forbidden");
224
225 let _ = send_forbidden_response(stream, connection);
226 return;
227 }
228
229 let file_path = if path == "/" || path == format!("/{}", self.config.index_file) {
230 format!("src/{}", self.config.index_file)
231 } else {
232 format!("{}{}", self.config.public_dir, path)
233 };
234
235 if Path::new(&file_path).exists() && Path::new(&file_path).is_file() {
236 let content_type = get_content_type(&self.config, &file_path);
237
238 let is_binary = self.is_binary_content_type(content_type);
239
240 if is_binary {
241 match fs::read(&file_path) {
242 Ok(contents) => {
243 let _ = self.logger.log_request(peer_addr, "GET", path, StatusCode::OK);
244 let _ = self.send_binary_response(stream, StatusCode::OK, content_type, &contents, connection);
245 }
246 Err(e) => {
247 let _ = self.logger.log_request(peer_addr, "GET", path, StatusCode::INTERNAL_SERVER_ERROR);
248 let _ = self.logger.log_message(&format!("Erreur de lecture du fichier binaire: {}", e));
249
250 self.stats_manager.increment_error_count("500 Internal Server Error");
251
252 let _ = send_internal_server_error_response(stream, connection);
253 }
254 }
255 } else {
256 match fs::read_to_string(&file_path) {
257 Ok(contents) => {
258 let _ = self.logger.log_request(peer_addr, "GET", path, StatusCode::OK);
259
260 let _ = send_get_response(stream, StatusCode::OK, content_type, contents, connection);
261 }
262 Err(e) => {
263 let _ = self.logger.log_request(peer_addr, "GET", path, StatusCode::INTERNAL_SERVER_ERROR);
264 let _ = self.logger.log_message(&format!("Erreur de lecture du fichier: {}", e));
265
266 self.stats_manager.increment_error_count("500 Internal Server Error");
267
268 let _ = send_internal_server_error_response(stream, connection);
269 }
270 }
271 }
272 } else {
273 let _ = self.logger.log_request(peer_addr, "GET", &path, StatusCode::NOT_FOUND);
274 let _ = self.logger.log_message(&format!("Fichier non trouvé: {}", file_path));
275
276 self.stats_manager.increment_error_count("404 Not Found");
277
278 self.send_not_found_response(stream, connection);
279 }
280 }
281
282 fn is_binary_content_type(&self, content_type: &str) -> bool {
292 content_type.starts_with("image/") ||
293 content_type.starts_with("audio/") ||
294 content_type.starts_with("video/") ||
295 content_type.starts_with("application/") && content_type != "application/javascript"
296 }
297
298 fn send_binary_response(&self,
312 stream: &mut TcpStream,
313 status_line: &str,
314 content_type: &str,
315 contents: &[u8],
316 connection: &str
317 ) -> std::io::Result<()> {
318 let length = contents.len();
319 let date = crate::utils::get_http_date();
320 let server = crate::utils::get_server_version();
321
322 let header = format!(
324 "{}\r\nServer: {}\r\nDate: {}\r\nContent-Length: {}\r\nContent-Type: {}\r\nConnection: {}\r\n\r\n",
325 status_line, server, date, length, content_type, connection
326 );
327
328 stream.write_all(header.as_bytes())?;
330
331 stream.write_all(contents)
333 }
334
335 fn handle_head_request(&self, peer_addr: &std::net::SocketAddr, path: &str, stream: &mut TcpStream, connection: &str) {
344 if path.contains("..") {
345 let _ = self.logger.log_request(peer_addr, "HEAD", path, StatusCode::FORBIDDEN);
346 let _ = self.logger.log_message(&format!("Tentative d'accès non autorisé: {}", path));
347
348 self.stats_manager.increment_error_count("403 Forbidden");
349
350 let _ = send_head_response(stream, StatusCode::FORBIDDEN, "text/html", connection);
351 return;
352 }
353
354 let file_path = if path == "/" || path == format!("/{}", self.config.index_file) {
355 format!("src/{}", self.config.index_file)
356 } else {
357 format!("{}{}", self.config.public_dir, path)
358 };
359
360 if Path::new(&file_path).exists() && Path::new(&file_path).is_file() {
361 let content_type = get_content_type(&self.config, &file_path);
362 let _ = self.logger.log_request(peer_addr, "HEAD", path, StatusCode::OK);
363
364 let _ = send_head_response(stream, StatusCode::OK, content_type, connection);
365 } else {
366 let _ = self.logger.log_request(peer_addr, "HEAD", path, StatusCode::NOT_FOUND);
367 let _ = self.logger.log_message(&format!("Fichier non trouvé: {}", file_path));
368
369 self.stats_manager.increment_error_count("404 Not Found");
370
371 let _ = send_head_response(stream, StatusCode::NOT_FOUND, "text/html", connection);
372 }
373 }
374
375 fn send_not_found_response(&self, stream: &mut TcpStream, connection: &str) {
382 let custom_404_path = format!("{}/{}", self.config.public_dir, self.config.not_found_page);
383
384 if Path::new(&custom_404_path).exists() && Path::new(&custom_404_path).is_file() {
385 match fs::read_to_string(&custom_404_path) {
386 Ok(contents) => {
387 let content_type = get_content_type(&self.config, &custom_404_path);
388 let _ = send_get_response(stream, StatusCode::NOT_FOUND, content_type, contents, connection);
389 }
390 Err(_) => {
391 let _ = send_not_found_response(stream, connection);
392 }
393 }
394 } else {
395 let _ = send_not_found_response(stream, connection);
396 }
397 }
398}
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403
404 #[test]
405 fn test_new_webserver() {
406 let config = ServerConfig::default();
407 let server = WebServer::new(config.clone());
408
409 assert_eq!(server.config.host, config.host);
410 assert_eq!(server.config.port, config.port);
411 assert_eq!(server.config.public_dir, config.public_dir);
412 }
413}