webby/server/
server.rs

1use std::{
2    collections::HashMap,
3    io::{Read, Write},
4    net::{TcpListener, TcpStream},
5};
6
7use log::{debug, error, info, trace};
8
9use crate::{request::http_method::HttpMethod, response::http_response::HttpResponse};
10
11pub type RouteFunc = fn() -> HttpResponse;
12
13struct RouteInfo {
14    method: HttpMethod,
15    callback: RouteFunc,
16}
17
18pub struct Server {
19    address: String,
20    routing_patterns: HashMap<String, RouteInfo>,
21}
22
23impl Server {
24    pub fn new(address: &str) -> Self {
25        Self {
26            address: address.to_owned(),
27            routing_patterns: HashMap::new(),
28        }
29    }
30
31    pub fn start_listening(self) -> Self {
32        trace!("About to start listening on {}", self.address);
33        match TcpListener::bind(&self.address) {
34            Ok(listener) => {
35                info!("Server running on \"{}\"", self.address);
36
37                for stream in listener.incoming() {
38                    match stream {
39                        Ok(mut stream) => self.handle_connection(&mut stream),
40                        Err(error) => error!("{}", error.to_string()),
41                    }
42                }
43            }
44            Err(error) => error!("{}", error.to_string()),
45        }
46
47        self
48    }
49
50    pub fn add_route(mut self, method: HttpMethod, pattern: &str, route_func: RouteFunc) -> Self {
51        trace!("Adding pattern \"{}\"", pattern);
52
53        if self.routing_patterns.contains_key(pattern) {
54            debug!(
55                "Pattern \"{}\" already exists, routing information will be overwritten",
56                pattern
57            );
58        }
59
60        self.routing_patterns.insert(
61            pattern.to_owned(),
62            RouteInfo {
63                method: method,
64                callback: route_func,
65            },
66        );
67
68        self
69    }
70
71    fn handle_connection(&self, stream: &mut TcpStream) {
72        let mut buffer = [0; 1024];
73        if stream.read(&mut buffer).is_err() {
74            return error!("Unable to read buffer from incoming TCP stream");
75        }
76
77        let route_to_execute = self.parse_request(&buffer);
78
79        if route_to_execute.is_none() {
80            trace!("Did not find a matching route to execute");
81            self.write_response(
82                stream,
83                HttpResponse::new().not_found().to_string().as_bytes(),
84            );
85        } else {
86            trace!("Executing matching route now");
87            let response = route_to_execute.unwrap();
88            self.write_response(stream, response.to_string().as_bytes());
89        }
90    }
91
92    fn parse_request(&self, buffer: &[u8]) -> Option<HttpResponse> {
93        let request_str = String::from_utf8_lossy(buffer).to_string();
94
95        let mut parts = request_str.split_whitespace();
96        let method = parts.next();
97        let route = parts.next();
98        let http_spec = parts.next();
99
100        if method.is_none() || route.is_none() || http_spec.is_none() {
101            error!("Malformed HTTP request");
102            return None;
103        }
104
105        // Naive route matching algorithm
106        for (pattern, route_info) in &self.routing_patterns {
107            if route.unwrap() == pattern {
108                if HttpMethod::from(method.unwrap()) == route_info.method {
109                    debug!(
110                        "Route \"{}\" matches pattern \"{}\"",
111                        route.unwrap(),
112                        pattern
113                    );
114                    return Some((route_info.callback)());
115                } else {
116                    debug!(
117                        "Route \"{}\" matches pattern \"{}\", but the method \"{}\" is not allowed",
118                        route.unwrap(),
119                        pattern,
120                        method.unwrap()
121                    );
122                    return Some(HttpResponse::new().method_not_allowed());
123                }
124            }
125        }
126
127        None
128    }
129
130    fn write_response(&self, stream: &mut TcpStream, data: &[u8]) {
131        if stream.write(data).is_err() {
132            return error!("Unable to write response to TCP stream");
133        }
134
135        if stream.flush().is_err() {
136            return error!("Unable to flush TCP stream");
137        }
138    }
139}