Adding all sorts of goodies.

This commit is contained in:
calcu1on
2025-04-27 11:34:30 -04:00
parent 45375e0fa3
commit 86d58c3f8a
15 changed files with 295 additions and 123 deletions

View File

@@ -3,8 +3,7 @@ use std::{
io::{prelude::*, BufReader},
net::TcpStream,
};
use crate::router;
use batflip::*;
#[allow(dead_code)]
struct IncomingRequest {
@@ -13,7 +12,7 @@ struct IncomingRequest {
// headers: Vec<String>
}
#[allow(unused_assignments)]
// Handle the incoming connection.
pub fn handle_connection(mut stream: TcpStream) {
// Create a new buffer reader with the stream data.
let buf_reader = BufReader::new(&stream);
@@ -36,9 +35,10 @@ pub fn handle_connection(mut stream: TcpStream) {
// Handle 404.
if inc_request.path.is_empty() {
response = return_response("404");
stream.write_all(response.as_bytes()).unwrap()
}
// Handle images.
// Handle images, else pages or assets.
if inc_request.path.contains("images") {
let mut public_route: String = "./public".to_string();
public_route.push_str(&inc_request.path);
@@ -49,52 +49,10 @@ pub fn handle_connection(mut stream: TcpStream) {
status_line,
image_data.len()
);
stream.write(response.as_bytes()).unwrap();
stream.write(&image_data).unwrap();
stream.write_all(response.as_bytes()).unwrap();
stream.write_all(&image_data).unwrap()
} else {
response = return_response(&inc_request.path);
stream.write(response.as_bytes()).unwrap();
stream.write_all(response.as_bytes()).unwrap()
}
}
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/index.html").unwrap();
},
&_ => {
// Need to get the proper response based on the given path.
status_line = "HTTP/1.1 200 OK".to_string();
contents = router::get_route_data(path_or_code);
// status_line = "HTTP/1.1 404 Not Found".to_string();
}
}
let length = contents.len();
format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}")
}
pub fn path_exists(path: &str) -> bool {
fs::metadata(path).is_ok()
}
#[allow(dead_code)]
fn get_header(headers: Vec<String>, 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
}

136
src/lib.rs Normal file
View File

@@ -0,0 +1,136 @@
use std::fs;
use std::{
sync::{mpsc, Arc, Mutex},
thread,
};
use regex::Regex;
#[allow(dead_code)]
pub struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
type Job = Box<dyn FnOnce() + Send + 'static>;
impl ThreadPool {
pub fn new(size: usize) -> ThreadPool {
assert!(size > 0);
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
}
pub fn process<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
#[allow(dead_code)]
struct Worker {
id: usize,
thread: thread::JoinHandle<()>,
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
let thread = thread::spawn(move || loop {
let job = receiver.lock().unwrap().recv().unwrap();
// println!("Worker {id} got a job; executing.");
job();
});
Worker { id, thread }
}
}
// Create the response for a request.
pub 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/pages/404.html").unwrap();
},
&_ => {
status_line = "HTTP/1.1 200 OK".to_string();
contents = get_route_data(path_or_code);
}
}
let length = contents.len();
format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}")
}
// Generate the data for the given route.
pub fn get_route_data(route: &str) -> String {
match route {
"/" => {
let file_contents = fs::read_to_string("./public/pages/index.html").unwrap();
hot_swap(&file_contents)
},
"/scorecards" => {
let file_contents = fs::read_to_string("./public/pages/scorecards.html").unwrap();
hot_swap(&file_contents)
},
"/add-scorecard" => {
let file_contents = fs::read_to_string("./public/pages/add-scorecard.html").unwrap();
hot_swap(&file_contents)
},
_ if route.contains("assets") => {
let mut public_route: String = "./public".to_string();
public_route.push_str(&route);
return fs::read_to_string(&public_route).unwrap()
},
&_ => {
return fs::read_to_string("./public/pages/404.html").unwrap();
}
}
}
// Swap dynamic sections of html template.
pub fn hot_swap(source_html: &str) -> String {
let mut updated_html: String = String::from(source_html);
let re = Regex::new(r"\{%.(?<name>.*).%\}").unwrap();
for section in re.captures_iter(source_html) {
let comp_content = retrieve_component(&section["name"]);
updated_html = updated_html.replace(&section[0], &comp_content);
}
updated_html
}
// Retrieve html markup from component.
pub fn retrieve_component(component: &str) -> String {
let mut comp_path = String::from("./public/components/");
let html_ext = String::from(".html");
comp_path.push_str(&component);
comp_path.push_str(&html_ext);
fs::read_to_string(&comp_path).unwrap()
}
// Validate if a given path exists.
pub fn path_exists(path: &str) -> bool {
fs::metadata(path).is_ok()
}
#[allow(dead_code)]
pub fn get_header(headers: Vec<String>, 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
}

View File

@@ -1,20 +1,17 @@
use std::net::TcpListener;
use batflip::ThreadPool;
// use std::thread;
pub mod connection;
mod router;
// Listen on port and return response.
fn main() {
let run_local = true;
let mut listener = TcpListener::bind("127.0.0.1:6942").unwrap();
if !run_local {
listener = TcpListener::bind("0.0.0.0:6942").unwrap();
}
let listener = TcpListener::bind("127.0.0.1:6942").unwrap();
let pool = ThreadPool::new(4);
for stream in listener.incoming() {
let stream = stream.unwrap();
connection::handle_connection(stream);
pool.process(move || {
connection::handle_connection(stream);
});
}
}

View File

@@ -1,46 +0,0 @@
use std::fs;
use std::collections::HashMap;
pub fn get_route_data(route: &str) -> String {
// @required - set valid routes here.
let route_map = HashMap::from([
("/", "./public/index.html"),
]);
dbg!(route_map.get(route));
// match route_map.get(route) {
// None => ,
// Some(page) => Some(page),
// };
// Some("test".to_string())
//
//
// High leve list of thigns to do
// 1. get the correct file to retrieve
// 2. parse the file and swap out any dynamic sections
// 3. return the string based response.
let route_file: String = match route {
"/" => {
"
return fs::read_to_string("./public/index.html").unwrap();
},
"/about" => {
return fs::read_to_string("./public/pages/about.html").unwrap();
}
_ if route.contains("assets") => {
let mut public_route: String = "./public".to_string();
public_route.push_str(&route);
return fs::read_to_string(&public_route).unwrap()
},
&_ => {
return fs::read_to_string("./public/404.html").unwrap();
}
}
// fs::read_to_string(route_file).unwrap();
}
// pub fn path_exists(path: &str) -> bool {
// fs::metadata(path).is_ok()
// }