diff --git a/Cargo.toml b/Cargo.toml index e173c71..e9407fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] base64 = "0.22.1" rand = "0.8" +sqlite = "0.36.1" diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..d42bc82 --- /dev/null +++ b/public/404.html @@ -0,0 +1,15 @@ + + + + Page Not Found + + + +

Page cannot be found

+

Sorry, this page cannot be found.

+ + diff --git a/src/connection.rs b/src/connection.rs new file mode 100644 index 0000000..93d41a0 --- /dev/null +++ b/src/connection.rs @@ -0,0 +1,117 @@ +use std::{ + fs, + io::{prelude::*, BufReader}, + net::TcpStream, +}; + +use crate::router; + +#[allow(dead_code)] +struct IncomingRequest { + req_type: String, + path: String + // headers: Vec +} + +#[allow(unused_assignments)] +pub fn handle_connection(mut stream: TcpStream) { + // Create a new buffer reader with the stream data. + let buf_reader = BufReader::new(&stream); + // Store each line in a vector. + let http_request: Vec<_> = buf_reader + .lines() + .map(|result| result.unwrap()) + .take_while(|line| !line.is_empty()) + .collect(); + // Get the first line of the stream which contains request data. + let request_line: String = http_request.first().expect("Not a string").to_string(); + // Split the request data on spaces into a vector. + let req_vec: Vec<&str> = request_line.split(" ").collect(); + println!("{:?}", http_request); + let inc_request = IncomingRequest { + req_type: req_vec[0].to_string(), + path: req_vec[1].to_string(), + }; + // Retrieve the api key from the request. + let api_key: String = get_header(http_request, "x-api-key"); + let mut response = String::new(); + // If not a GET request, or missing API key, throw 404. + if inc_request.req_type != "GET" || api_key.is_empty() { + response = return_response("404"); + } + else { + // Get allowed api key from text file. + let mut keyfile: Vec = fs::read_to_string("./keys.txt").into_iter().collect(); + // Get the first item, for now. @todo make this more dynamic. + keyfile[0].pop().expect("Keyfile not found").to_string(); + if api_key == keyfile[0] { + // Here we need to look up the route by path. + if inc_request.path.is_empty() { + response = return_response("404"); + } + else { + response = return_response(&inc_request.path); + } + // response = return_response("200"); + } else { + response = return_response("404"); + } + } + stream.write_all(response.as_bytes()).unwrap(); +} + +fn get_api_key(headers: Vec) -> String { + let mut api_key = String::new(); + for header in headers { + // split header, strip whitespace, check if exists. + let split_header: Vec<&str> = header.split(":").collect(); + let header_key: String = split_header.first().expect("Nothing here").to_string(); + if header_key == "x-api-key" { + api_key = split_header[1].to_string(); + } + } + api_key +} + +fn get_header(headers: Vec, needle: &str) -> String { + let mut value = String::new(); + for header in headers { + let split_header: Vec<&str> = header.split(":").collect(); + let header_key: String = split_header.first().expect("Nothing here").to_string(); + if header_key == needle { + value = split_header[1].to_string(); + } + } + value +} + +fn return_response(path_or_code: &str) -> String { + let status_line: String; + let contents: String; + + match path_or_code { + "404" => { + status_line = "HTTP/1.1 404 Not Found".to_string(); + contents = fs::read_to_string("./public/404.html").unwrap(); + }, + "200" => { + status_line = "HTTP/1.1 200 OK".to_string(); + contents = fs::read_to_string("./public/200.json").unwrap(); + }, + &_ => { + // Need to get the proper response based on the given path. + let has_route = router::route_exists(path_or_code); + if has_route { + status_line = "HTTP/1.1 200 OK".to_string(); + contents = router::get_route_data(path_or_code); + } + else { + status_line = "HTTP/1.1 404 Not Found".to_string(); + contents = fs::read_to_string("./public/404.html").unwrap(); + } + } + } + + let length = contents.len(); + format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}") +} diff --git a/src/main.rs b/src/main.rs index 909405f..987cf2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,7 @@ -use std::{ - fs, - io::{prelude::*, BufReader}, - net::{TcpListener, TcpStream}, -}; +use std::net::TcpListener; -#[allow(dead_code)] -struct IncomingRequest { - req_type: String, - path: String -} +pub mod connection; +mod router; // Listen on port and return response. fn main() { @@ -21,81 +14,7 @@ fn main() { for stream in listener.incoming() { let stream = stream.unwrap(); - handle_connection(stream); + connection::handle_connection(stream); } } -#[allow(unused_assignments)] -fn handle_connection(mut stream: TcpStream) { - let buf_reader = BufReader::new(&stream); - let http_request: Vec<_> = buf_reader - .lines() - .map(|result| result.unwrap()) - .take_while(|line| !line.is_empty()) - .collect(); - - let request_line: String = http_request.first().expect("Not a string").to_string(); - let req_vec: Vec<&str> = request_line.split(" ").collect(); - let inc_request = IncomingRequest { - req_type: req_vec[0].to_string(), - path: req_vec[1].to_string(), - }; - let api_key: String = get_api_key(http_request); - let mut response = String::new(); - // If not a GET request, or missing API key, throw 404. - if inc_request.req_type != "GET" || api_key.is_empty() { - response = return_response("404"); - } - else { - let mut keyfile: Vec = fs::read_to_string("./keys.txt").into_iter().collect(); - keyfile[0].pop().expect("Not a string").to_string(); - if api_key == keyfile[0] { - response = return_response("200"); - } else { - response = return_response("404"); - } - } - stream.write_all(response.as_bytes()).unwrap(); -} - -// get the apy key from the headers. -fn get_api_key(headers: Vec) -> String { - let mut api_key = String::new(); - - for header in headers { - // split header, strip whitespace, check if exists. - let split_header: Vec<&str> = header.split(":").collect(); - let header_key: String = split_header.first().expect("Nothing here").to_string(); - if header_key == "x-api-key" { - api_key = split_header[1].to_string(); - } - } - - api_key -} - -#[allow(unused_assignments)] -fn return_response(code: &str) -> String { - let mut response = String::new(); - match code { - "404" => { - let status_line = "HTTP/1.1 404 Not Found"; - let contents = fs::read_to_string("./public/404.json").unwrap(); - let length = contents.len(); - - response = - format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); - }, - "200" => { - let status_line = "HTTP/1.1 200 OK"; - let contents = fs::read_to_string("./public/200.json").unwrap(); - let length = contents.len(); - - response = - format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}"); - }, - &_ => todo!() - } - - response -} diff --git a/src/router.rs b/src/router.rs new file mode 100644 index 0000000..028bc39 --- /dev/null +++ b/src/router.rs @@ -0,0 +1,38 @@ +use std::collections::HashMap; + +pub fn get_route_data(route: &str) -> String { + let route_data: String; + match route { + "/fetch" => { + route_data = fetch_asset(); + } + &_ => todo!(), + } + route_data +} + +pub fn route_exists(route: &str) -> bool { + let existing_routes = get_routes(); + if existing_routes.contains_key(route) { + true + } + else { + false + } +} + +fn get_routes() -> HashMap { + let mut routes = HashMap::new(); + routes.insert("/fetch".to_string(), "fetch_asset".to_string()); + routes +} + +fn fetch_asset() -> String { + let asset = r#"{ + "title": "this is a title", + "id": 2, + "url": "https://www.espn.com + }"#; + + asset.to_string() +}