1use std::io::prelude::*;
2use std::{env, fs};
3use std::{
4 net::{TcpListener, TcpStream},
5 thread,
6};
7
8use models::content_type::ContentType;
9use utils::color::Color;
10
11mod models;
12mod utils;
13
14pub fn run(port: Option<&str>) {
15 let port = if let Some(port) = port { port } else { "3000" };
16
17 let listener = TcpListener::bind(&format!("127.0.0.1:{}", &port)).expect(&format!(
18 "{}",
19 Color::Red.text(&format!("Failed to listen on 127.0.0.1:{}.", &port))
20 ));
21
22 let current_dir = match env::current_dir() {
23 Ok(path) => path,
24 Err(e) => {
25 eprintln!(
26 "{}",
27 Color::Red.text(&format!(
28 "[ERROR] the current directory could not be found: {}",
29 e
30 ))
31 );
32 return;
33 }
34 };
35 println!(
36 "Serving path {}",
37 Color::Cyan.text(¤t_dir.to_string_lossy())
38 );
39 println!("Available at:");
40 println!(
41 " {}",
42 Color::Green.text(&format!("http://127.0.0.1:{}", &port))
43 );
44 println!("\nQuit by pressing CTRL-C");
45
46 for stream in listener.incoming() {
47 match stream {
48 Ok(stream) => {
49 thread::spawn(|| {
50 handle_connection(stream);
51 });
52 }
53 Err(e) => {
54 eprintln!(
55 "{}",
56 Color::Red.text(&format!("[ERROR] Connection failed: {}", e))
57 );
58 }
59 }
60 }
61}
62
63fn handle_connection(mut stream: TcpStream) {
64 let mut buffer = [0; 1024];
65
66 match stream.read(&mut buffer) {
67 Ok(_) => {
68 let request = String::from_utf8_lossy(&buffer[..]);
69
70 if request.is_empty() {
71 let res = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
72 stream.write_all(res.as_bytes()).expect(&format!(
73 "{}",
74 Color::Red.text("[ERROR] Failed to return 400 Bad Request")
75 ));
76 return;
77 }
78
79 let request: Vec<&str> = request.lines().collect();
80 let request = if let Some(request) = request.first() {
81 request
82 } else {
83 let res = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
84 stream.write_all(res.as_bytes()).expect(&format!(
85 "{}",
86 Color::Red.text("[ERROR] Failed to return 400 Bad Request")
87 ));
88 return;
89 };
90 let request: Vec<&str> = request.split(' ').collect();
91 let resource = if let Some(resource) = request.get(1) {
92 resource
93 } else {
94 let res = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
95 stream.write_all(res.as_bytes()).expect(&format!(
96 "{}",
97 Color::Red.text("[ERROR] Failed to return 400 Bad Request")
98 ));
99 return;
100 };
101 let resource: Vec<&str> = resource.split('?').collect();
102 let resource = if let Some(resource) = resource.get(0) {
103 resource
104 } else {
105 let res = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
106 stream.write_all(res.as_bytes()).expect(&format!(
107 "{}",
108 Color::Red.text("[ERROR] Failed to return 400 Bad Request")
109 ));
110 return;
111 };
112
113 let file_path = if 1 < resource.len() {
114 let resource = &resource[1..];
115 resource
116 } else {
117 "index.html"
118 };
119
120 let contents = if let Ok(contents) = fs::read(&format!("./{}", file_path)) {
121 println!("[INFO] {} could be found", resource);
122 contents
123 } else {
124 eprintln!(
125 "{}",
126 Color::Red.text(&format!("[ERROR] {} could not be found", resource))
127 );
128 let res = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n";
129 stream.write_all(res.as_bytes()).expect(&format!(
130 "{}",
131 Color::Red.text("[ERROR] Failed to return 400 Bad Request")
132 ));
133 return;
134 };
135
136 let content_type = ContentType::new(file_path);
137
138 let response_header = format!(
139 "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nContent-Type: {}\r\nCross-Origin-Opener-Policy: same-origin\r\nCross-Origin-Embedder-Policy: require-corp\r\n\r\n",
140 contents.len(),
141 content_type.get_content_type()
142 );
143
144 stream
145 .write_all(response_header.as_bytes())
146 .expect(&format!(
147 "{}",
148 Color::Red.text("[ERROR] Failed to send header")
149 ));
150
151 stream.write_all(&contents).expect(&format!(
152 "{}",
153 Color::Red.text("[ERROR] Failed to send file contents")
154 ));
155 }
156 Err(e) => {
157 eprintln!(
158 "{}",
159 Color::Red.text(&format!("[ERROR] Failed to read from connection: {}", e))
160 );
161 }
162 }
163}