1use std::net::{TcpListener, TcpStream};
2use std::io::{Read, Write, BufReader, BufRead};
3use std::sync::Arc;
4use std::collections::HashMap;
5
6use super::routes::Routes;
7use super::http::{HttpMethod, Request};
8use super::thread_pool::{ThreadPool};
9use crate::core::config::get_configuration;
10
11struct RequestHandler;
12
13fn read_static(file_pth: &str) -> impl Fn(&str) -> String + '_ {
14 move |pref| -> String {
15 use std::fs;
16 let content = fs::read_to_string(format!("{}/{}", pref, file_pth))
17 .unwrap_or_else(|error| { error.to_string() });
18 content
19 }
20}
21
22impl RequestHandler {
23 fn handle_request(&self, stream: TcpStream, routes: Arc<Routes>) {
24 let mut reader = BufReader::new(stream);
25
26 let request = self.parse_request(&mut reader);
27
28 log::info!("Request: {:?} {}", request.method, request.url);
29 if !request.body.is_empty() {
30 log::info!("{}", request.body);
31 }
32
33 let config_reader = get_configuration();
34 let config = config_reader.inner.lock().unwrap();
35 let tmp_base_path = config.base_path.to_string();
36 let prefix = config.static_uri_pref.to_string();
37 drop(config);
38
39 let content = if request.url.starts_with(&prefix) {
40 read_static(&request.url)(tmp_base_path.as_str())
41 } else {
42 (routes.get_route(request.url.as_ref()).render_action)(request.url.as_ref())
43 };
44
45 let response = format!(
46 "HTTP/1.1 200 OK\r\n\r\n{}",
47 content
48 );
49 log::info!("Response: {}", response);
50
51 let mut reader_stream = reader.into_inner();
52 let result = reader_stream.write(response.as_bytes());
53 result.unwrap_or_else(|error| {
54 log::info!("{}", error);
55 0
56 });
57
58 reader_stream.flush().unwrap_or_else(|error| {
59 log::info!("{}", error);
60 });
61 }
62
63 fn parse_request(&self, reader: &mut BufReader<TcpStream>) -> Request {
66 let mut lines = reader.by_ref().lines();
67
68 let method_line = lines.next().unwrap().unwrap();
73 let method = method_line.split(" ").collect::<Vec<&str>>();
74 let http_method = HttpMethod::parse(method[0]);
75
76 let mut headers = HashMap::<String, String>::new();
77 for line in lines {
78 let l = line.unwrap();
79
80 if l == String::from("") {
82 break;
83 }
84
85 let parts = l.split(":").collect::<Vec<&str>>();
86 if parts.len() == 2 {
87 headers.insert(parts[0].to_string(), parts[1].to_string());
88 }
89 };
90
91 let mut url = if method.len() > 1 {
92 method[1].to_string()
93 } else {
94 String::new()
95 };
96
97 if !url.ends_with("/")
98 && !url.ends_with(".css")
99 && !url.ends_with(".js")
100 && !url.ends_with(".jpg")
101 {
102 url = format!("{}/", url);
103 }
104
105 Request {
106 method: http_method,
107 url,
108 headers,
109 body: String::new(),
110 }
111 }
112}
113
114pub struct Ctchi {
115 routes: Routes,
116}
117
118impl Ctchi {
119 pub fn new(routes: Routes) -> Ctchi {
127 Ctchi {
128 routes
129 }
130 }
131
132 pub fn start(self) -> std::io::Result<()> {
166 let config_reader = get_configuration();
167 let config = config_reader.inner.lock().unwrap();
168
169 let listener = TcpListener::bind(&config.bind_path)?;
170 drop(config);
171 let routes = Arc::new(self.routes);
172
173 let pool = ThreadPool::new(4);
174
175 for stream in listener.incoming() {
176 let stream = stream.unwrap();
177 let r = routes.clone();
178
179 pool.execute(|| {
180 let handler = RequestHandler {};
181
182 handler.handle_request(stream, r);
183 });
184 }
185 Ok(())
186 }
187}