http-salient 0.1.1

Simple http webserver
Documentation
use std::{
    io::{BufRead, BufReader, Write},
    net::{TcpListener, TcpStream},
    thread,
};

use config::Config;
mod config;

use response::Response;
mod response;

mod page;

use cache::Cache;
mod cache;

mod content;

pub struct Server {
    listener: TcpListener,
    config: Config,
    cache: Option<Cache>,
    requests_handled: u128,
}

impl Server {
    pub fn new() -> Self {
        let config: Config = confy::load("salient", None).unwrap();
        let listener = TcpListener::bind(&config.address).unwrap();

        let cache: Option<Cache> = if config.caching {
            Some(Cache::new())
        } else {
            None
        };

        let requests_handled = 0u128;

        Server {
            listener,
            config,
            cache,
            requests_handled,
        }
    }

    pub fn run(&'static mut self) {
        let mut threads: Vec<thread::JoinHandle<_>> = Vec::new();
        let output_delay = self.config.statistics_output_delay as u128;
        for stream in self.listener.incoming() {
            let stream = match stream {
                Ok(stream) => stream,
                Err(_) => continue,
            };

            if self.config.statistics {
                self.requests_handled += 1;
                if self.requests_handled % output_delay == 0 {
                    println!("Requests handled: {}", self.requests_handled);
                }
            }

            loop {
                if threads.len() < self.config.thread_limit {
                    break;
                } else {
                    let _ = threads.remove(0).join();
                }
            }

            threads.push(thread::spawn(|| {
                Self::handle_connection(stream, &self.config.double_dot_defence, &self.cache);
            }));
        }
    }

    fn handle_connection(mut stream: TcpStream, double_dot_defence: &bool, cache: &Option<Cache>) {
        let buf_reader = BufReader::new(&mut stream);
        let http_request: Vec<_> = buf_reader
            .lines()
            .map(|result| result.unwrap())
            .take_while(|line| !line.is_empty())
            .collect();

        let request_type = http_request[0].split(' ').nth(0).unwrap_or("GET");
        let path = http_request[0].split(' ').nth(1).unwrap_or("/");

        if request_type == "GET" {
            let path = content::format_path(path, &double_dot_defence);
            let response: Response = match cache {
                None => content::page_from_file(&path),
                Some(cache) => content::page_from_cache(&cache, &path),
            };
            let _ = stream.write_all(response.bytes());
        } else if request_type == "POST" {
            match path {
                _ => {}
            }
        }
    }
}