httpageboy/
request.rs

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  // Divide la solicitud en líneas
32  let lines: Vec<&str> = request.split("\r\n").collect();
33
34  // Busca el índice de la línea en blanco que separa los headers del body
35  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  // println!("REQUEST:\n{}", req);
101  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}