Skip to main content

lava_http/server/
server.rs

1use std::{
2    net::{
3        TcpListener, 
4        TcpStream
5    }, 
6    io::{
7        Read, 
8        Write, SeekFrom
9    }, 
10    fmt::format, string
11};
12
13use crate::{
14    request::parse::parse_request_header,
15    request::req::Request,
16    server::route::Route,
17    response::res::Response
18};
19
20use crate::server::servefile::send_static_file;
21
22pub struct Server{
23    pub host:      &'static str,
24    pub port:      i32,
25    pub routes:    Vec<Route>
26}
27
28impl Server{
29    fn bind(&self) -> TcpListener{
30
31        let (
32            port_min, 
33            port_max
34        ) = (1023, 65535);
35
36        if self.port < port_min || self.port > port_max{
37            panic!("Invalid port");
38        }
39
40        let addr: String = format!(
41            "{}:{}", self.host, self.port
42        );
43
44        let bind: TcpListener = TcpListener::bind(
45            addr
46        ).expect(
47            "Error binding socket"
48        );
49
50        const VOLCANO_EMOJI: char = '\u{1F30B}';
51        println!("\r\n\r\nThe volcano is live on port {}! {VOLCANO_EMOJI} {VOLCANO_EMOJI} {VOLCANO_EMOJI}\r\n\r\n", self.port);
52
53        return bind;
54    }
55
56    /// Listens to server on given host and port
57    /// 
58    /// Example
59    /// ```
60    /// let server: Server = Server{
61    ///     host: "127.0.0.1",
62    ///     port: 5000,
63    ///     routes: routes
64    /// };
65    /// ```
66    pub fn erupt(&self){
67        let listener: TcpListener = self.bind();
68        
69        loop {
70            let mut stream: TcpStream;
71
72            match listener.accept(){
73                Ok(conn) => {
74                    stream = conn.0;
75                }
76                Err(err) => { panic!("{}", err) }
77            };
78
79            let request: Request = parse_request_header(
80                read_connection(&stream)
81            );
82
83            let mut res: Response = Response { 
84                status: (200), 
85                body: (String::from("")), 
86                headers: (String::from("")) 
87            };
88
89            if request.path.ends_with(".css") || request.path.ends_with(".js"){
90                res = send_static_file(request.path);
91            }
92            else{ 
93                res = handle_connection(request, &self.routes);
94            }
95
96            let response_header: String = format!(
97                "HTTP/1.1 {}\r\n{}\r\n\r\n{}", 
98                res.get_status(), 
99                res.get_header(), 
100                res.get_body()
101            );
102
103            match stream.write_all(response_header.as_bytes()){
104                Ok(_) => {}
105                Err(err) => { panic!("{}", err) }
106            }
107
108            match stream.flush(){
109                Ok(_) => {}
110                Err(err) => { panic!("{}", err) }
111            }
112        }
113    }
114}
115
116fn read_connection(mut stream: &TcpStream) -> String{
117    const BUFFER_SIZE: usize = 4096;
118    let mut buffer: [u8; 4096] = [0; BUFFER_SIZE];
119
120    let mut data: Vec<u8> = vec![];
121
122    loop{
123
124        let bytes_read: usize = stream.read(&mut buffer).unwrap();
125
126        data.extend_from_slice(&buffer[..bytes_read]);
127
128        if bytes_read < BUFFER_SIZE{
129            break;
130        }
131    }
132
133    let request_header: String = String::from(
134        core::str::from_utf8(&data).unwrap()
135    );
136    
137    return request_header;
138}
139
140fn handle_connection(
141    req:        Request, 
142    routes:     &Vec<Route>
143) -> Response{
144
145    let mut res: Response = Response{
146        status: 200, 
147        body: String::from(""), 
148        headers: String::from("")
149    };
150
151    for route in routes{
152        if req.path != route.path{
153            continue;
154        }
155        else if req.method != route.method{
156            continue;
157        }
158        res = route.get_func()(res, &req);
159    }
160
161    return res;
162}
163
164