1use std::collections::HashMap;
2use std::fmt::{Display, Formatter, Result};
3use std::io::Read;
4use std::net::TcpStream;
5
6use crate::request_handler::Rh;
7use crate::request_type::Rt;
8use crate::response::Response;
9use crate::status_code::StatusCode;
10use crate::utils::{get_content_type_quick, secure_path};
11
12pub struct Request {
13 pub method: String,
14 pub path: String,
15 pub version: String,
16 pub headers: Vec<(String, String)>,
17 pub body: String,
18}
19
20impl Display for Request {
21 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
22 write!(
23 f,
24 "Method: {}\nPath: {}\nVersion: {}\nHeaders: {:#?},\nBody: {}\n",
25 self.method, self.path, self.version, self.headers, self.body
26 )
27 }
28}
29
30fn request_disassembly(request: String) -> Request {
31 let lines: Vec<&str> = request.split("\r\n").collect();
33
34 let mut blank_line_index = 0;
36 for (i, line) in lines.iter().enumerate() {
37 if line.trim().is_empty() {
38 blank_line_index = i;
39 break;
40 }
41 }
42
43 let temp_headers = lines[..blank_line_index].join("\r\n");
44 let mut parsed_headers = Vec::new();
45 for header_line in temp_headers.lines() {
46 let header_parts: Vec<&str> = header_line.split(": ").collect();
47 if header_parts.len() == 2 {
48 let header_name = header_parts[0].to_string();
49 let header_value = header_parts[1].to_string();
50 parsed_headers.push((header_name, header_value));
51 }
52 }
53 let headers = parsed_headers;
54 let body = lines[blank_line_index + 1..].join("\r\n");
55 let split_request: Vec<&str> = request.split_whitespace().collect();
56 let method: String = split_request[0].to_string();
57 let path: String = split_request[1].to_string();
58 let version: String = split_request[2].to_string();
59
60 return Request {
61 method,
62 path,
63 version,
64 headers,
65 body,
66 };
67}
68
69pub fn stream_to_request(mut stream: &TcpStream) -> Request {
70 let mut buf = [0; 1024];
71 stream.read(&mut buf).unwrap();
72 let raw = String::from_utf8_lossy(&buf).to_string();
73 request_disassembly(raw)
74}
75
76pub fn handle_file_request(path: &String, allowed: &[String]) -> Response {
77 for base in allowed {
78 if let Some(real_path) = secure_path(base, path.as_str()) {
79 println!("📄 buscando archivo en: {:?}", real_path);
80 if let Ok(data) = std::fs::read(&real_path) {
81 return Response {
82 status: StatusCode::Ok.to_string(),
83 content_type: get_content_type_quick(&real_path),
84 content: data,
85 };
86 } else {
87 println!("❌ archivo no encontrado: {:?}", real_path);
88 }
89 break;
90 }
91 }
92 Response::new()
93}
94
95pub fn handle_request(
96 req: &Request,
97 routes: &HashMap<String, Rh>,
98 file_bases: &[String],
99) -> Option<Response> {
100 let key = format!("{}|{}", req.path, req.method);
102
103 let mut output = None;
104 if let Some(h) = routes.get(&key) {
105 output = Some((h.handler)(req));
106 } else if req.method == Rt::GET.to_string() {
107 output = Some(handle_file_request(&req.path, file_bases));
108 }
109 output
110}