webserver_colin_ugo/server/
handler.rs

1use crate::server::http::HttpMethod;
2use crate::utils::get_content_type;
3use std::fs;
4use std::io::{BufRead, BufReader, Write};
5use std::net::{TcpListener as TCPListener, TcpStream};
6use std::path::Path;
7
8/// Cette fonction démarre le serveur et écoute les connexions entrantes.
9pub fn run() {
10    let listener = TCPListener::bind("localhost:8080").unwrap();
11
12    for stream in listener.incoming() {
13        let stream = stream.unwrap();
14
15        handle_connection(stream);
16    }
17}
18
19/// Cette fonction gère la connexion entrante et envoie la réponse appropriée.
20///
21/// # Arguments
22///
23/// * `stream` - Une instance TcpStream représentant la connexion entrante.
24fn handle_connection(mut stream: TcpStream) {
25    let buf_reader = BufReader::new(&stream);
26    let http_request: Vec<_> = buf_reader
27        .lines()
28        .map(|result| result.unwrap())
29        .take_while(|line| !line.is_empty())
30        .collect();
31
32    let get_request = http_request.first().unwrap();
33    let mut parts = get_request.split_whitespace();
34    let method = parts.next().unwrap();
35    let path = parts.next().unwrap();
36
37    let connection_header = http_request
38        .iter()
39        .find(|line| line.to_lowercase().starts_with("connection:"))
40        .map(|line| line.split(':').nth(1).unwrap().trim().to_lowercase())
41        .unwrap_or_else(|| "close".to_string());
42
43    match HttpMethod::from_str(method) {
44        Some(HttpMethod::GET) => handle_get_request(path, &mut stream, &connection_header),
45        Some(HttpMethod::HEAD) => handle_head_request(path, &mut stream, &connection_header),
46        _ => handle_method_not_allowed(&mut stream, &connection_header),
47    }
48}
49
50/// Gère les requêtes GET.
51///
52/// # Arguments
53///
54/// * `path` - Le chemin demandé.
55/// * `stream` - Le TcpStream pour écrire la réponse.
56/// * `connection` - L'en-tête de connexion.
57fn handle_get_request(path: &str, stream: &mut TcpStream, connection: &str) {
58    let file_path = format!("public{}", path);
59    let (status_line, contents, content_type) = if Path::new(&file_path).exists() {
60        match fs::read_to_string(&file_path) {
61            Ok(contents) => {
62                let content_type = get_content_type(&file_path);
63                ("HTTP/1.1 200 OK", contents, content_type)
64            }
65            Err(_) => {
66                let status_line = "HTTP/1.1 500 Internal Server Error";
67                let contents = "<h1>500 Internal Server Error</h1>".to_string();
68                (status_line, contents, "text/html")
69            }
70        }
71    } else {
72        let status_line = "HTTP/1.1 404 Not Found";
73        let contents = "<h1>404 Not Found</h1>".to_string();
74        (status_line, contents, "text/html")
75    };
76
77    let length = contents.len();
78
79    let response = format!(
80        "{}\r\nContent-Length: {}\r\nContent-Type: {}\r\nConnection: {}\r\n\r\n{}",
81        status_line, length, content_type, connection, contents
82    );
83
84    stream.write_all(response.as_bytes()).unwrap();
85}
86
87/// Gère les requêtes HEAD.
88///
89/// # Arguments
90///
91/// * `path` - Le chemin demandé.
92/// * `stream` - Le TcpStream pour écrire la réponse.
93/// * `connection` - L'en-tête de connexion.
94fn handle_head_request(path: &str, stream: &mut TcpStream, connection: &str) {
95    let file_path = format!("public{}", path);
96    let (status_line, content_type) = if Path::new(&file_path).exists() {
97        match fs::read_to_string(&file_path) {
98            Ok(_) => {
99                let content_type = get_content_type(&file_path);
100                ("HTTP/1.1 200 OK", content_type)
101            }
102            Err(_) => {
103                let status_line = "HTTP/1.1 500 Internal Server Error";
104                let content_type = "text/html";
105                (status_line, content_type)
106            }
107        }
108    } else {
109        let status_line = "HTTP/1.1 404 Not Found";
110        let content_type = "text/html";
111        (status_line, content_type)
112    };
113
114    let response = format!(
115        "{}\r\nContent-Length: 0\r\nContent-Type: {}\r\nConnection: {}\r\n\r\n",
116        status_line, content_type, connection
117    );
118
119    stream.write_all(response.as_bytes()).unwrap();
120}
121
122/// Gère les requêtes avec des méthodes qui ne sont pas autorisées.
123///
124/// # Arguments
125///
126/// * `stream` - Le TcpStream pour écrire la réponse.
127fn handle_method_not_allowed(stream: &mut TcpStream, connection: &str) {
128    let status_line = "HTTP/1.1 405 Method Not Allowed";
129    let response = format!("{}\r\nConnection: {}\r\n\r\n", status_line, connection);
130    stream.write_all(response.as_bytes()).unwrap();
131}