webserver_colin_ugo/server/
handler.rs1use 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
8pub 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
19fn 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
50fn 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
87fn 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
122fn 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}