http_salient/
lib.rs

1use std::{
2    io::{BufRead, BufReader, Write},
3    net::{TcpListener, TcpStream},
4    thread,
5};
6
7use config::Config;
8mod config;
9
10use response::Response;
11mod response;
12
13mod page;
14
15use cache::Cache;
16mod cache;
17
18mod content;
19
20pub struct Server {
21    listener: TcpListener,
22    config: Config,
23    cache: Option<Cache>,
24    requests_handled: u128,
25}
26
27impl Server {
28    pub fn new() -> Self {
29        let config: Config = confy::load("salient", None).unwrap();
30        let listener = TcpListener::bind(&config.address).unwrap();
31
32        let cache: Option<Cache> = if config.caching {
33            Some(Cache::new())
34        } else {
35            None
36        };
37
38        let requests_handled = 0u128;
39
40        Server {
41            listener,
42            config,
43            cache,
44            requests_handled,
45        }
46    }
47
48    pub fn run(&'static mut self) {
49        let mut threads: Vec<thread::JoinHandle<_>> = Vec::new();
50        let output_delay = self.config.statistics_output_delay as u128;
51        for stream in self.listener.incoming() {
52            let stream = match stream {
53                Ok(stream) => stream,
54                Err(_) => continue,
55            };
56
57            if self.config.statistics {
58                self.requests_handled += 1;
59                if self.requests_handled % output_delay == 0 {
60                    println!("Requests handled: {}", self.requests_handled);
61                }
62            }
63
64            loop {
65                if threads.len() < self.config.thread_limit {
66                    break;
67                } else {
68                    let _ = threads.remove(0).join();
69                }
70            }
71
72            threads.push(thread::spawn(|| {
73                Self::handle_connection(stream, &self.config.double_dot_defence, &self.cache);
74            }));
75        }
76    }
77
78    fn handle_connection(mut stream: TcpStream, double_dot_defence: &bool, cache: &Option<Cache>) {
79        let buf_reader = BufReader::new(&mut stream);
80        let http_request: Vec<_> = buf_reader
81            .lines()
82            .map(|result| result.unwrap())
83            .take_while(|line| !line.is_empty())
84            .collect();
85
86        let request_type = http_request[0].split(' ').nth(0).unwrap_or("GET");
87        let path = http_request[0].split(' ').nth(1).unwrap_or("/");
88
89        if request_type == "GET" {
90            let path = content::format_path(path, &double_dot_defence);
91            let response: Response = match cache {
92                None => content::page_from_file(&path),
93                Some(cache) => content::page_from_cache(&cache, &path),
94            };
95            let _ = stream.write_all(response.bytes());
96        } else if request_type == "POST" {
97            match path {
98                _ => {}
99            }
100        }
101    }
102}