rocal_dev_server/
lib.rs

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(&current_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}