From 3a738ba31ffa5c7a298850cb205570fda784e9e8 Mon Sep 17 00:00:00 2001 From: "Dawid J. Kubis" Date: Fri, 11 Mar 2022 18:09:16 +0100 Subject: [PATCH] and he stood there, atop a hill, and he heard the screams of threads panicking, seen he saw the sweating of poisoned mutexes, and he felt the smell of reset connections. that's when he knew he was in hell. --- src/errors.rs | 0 src/http.rs | 16 ++++++++++ src/main.rs | 76 +++++++++++++++++++++++++++++++++++----------- src/post.rs | 16 +++++----- src/www/index.html | 10 +++++- 5 files changed, 90 insertions(+), 28 deletions(-) create mode 100644 src/errors.rs diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/http.rs b/src/http.rs index 81b38c5..8b75b3f 100644 --- a/src/http.rs +++ b/src/http.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::fmt; use std::str::{FromStr, Lines}; @@ -33,11 +34,25 @@ impl Status { } } +impl Request { + pub fn form(&self) -> HashMap<&str, &str> { + let mut hashmap = HashMap::new(); + self.body.split("&") + .map(|x| x.split("=")) + .for_each( + |mut x| {hashmap.insert(x.next().unwrap(), x.next().unwrap());} + // fix unwrap hell + ); + hashmap + } +} + // TODO proper errors impl FromStr for Request { type Err = String; fn from_str(s: &str) -> Result { + dbg!(&s); let mut iter = s.lines(); let mut first = iter.next().unwrap().split_whitespace(); let method = first.next().unwrap(); @@ -91,3 +106,4 @@ impl fmt::Display for Response { ) } } + diff --git a/src/main.rs b/src/main.rs index bcd4460..4947e62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,8 @@ use threadpool::ThreadPool; // statically linked index and favicon const INDEX: &'static str = include_str!("www/index.html"); const FAVICON: &'static [u8] = include_bytes!("www/favicon.ico"); +const STYLE: &'static str = include_str!("www/style.css"); +const FAQ: &'static str = include_str!("www/faq.html"); // represents command line arguments #[derive(StructOpt, Debug)] @@ -44,15 +46,29 @@ lazy_static! { static ref OPT: Opt = Opt::from_args(); } +#[derive(Debug)] +struct State { + db: sled::Db, + id_counter: u64, + // cache here? +} + +impl State { + fn next_id(&mut self) -> u64 { + let id = self.id_counter; + self.id_counter += 1; + id + } +} + /// top level handling of requests -fn handle(request: Request, database: Arc>, ip: SocketAddr) -> Response { - let database = database.lock().unwrap(); +fn handle(request: Request, state: Arc>) -> Response { + let state = state.lock().unwrap(); match request.method.as_str() { - "GET" => get(&request.uri, database), + "GET" => get(&request.uri, state), "POST" => { - println!("{:?}", request); - Response::new(Status::Ok, vec![], "".to_string()) + post(request, state) }, // check admin hash "DELETE" => Response::new(Status::Ok, vec![], "".to_string()), @@ -62,13 +78,14 @@ fn handle(request: Request, database: Arc>, ip: SocketAddr) -> R /// get "/" returns head of every thread /// or returns posts from thread with id in uri -fn get(path: &str, database: MutexGuard) -> Response { +fn get(path: &str, state: MutexGuard) -> Response { match path { // list threads in this case "/" => { let content = content( "index", - &database + &state + .db .iter() .map(|x| x.unwrap()) .map(|x| (x.0, deserialize::(&x.1).unwrap())) @@ -78,12 +95,18 @@ fn get(path: &str, database: MutexGuard) -> Response { Response::new(Status::Ok, vec![], content) } + // TODO /css /faq /favicon.ico + + "/css" => Response::new(Status::Ok, vec![], String::from(STYLE)), + + "/faq" => Response::new(Status::Ok, vec![], String::from(FAQ)), + // list specific thread here // FIXME unwrap hell s => { let content = content( s, - &deserialize::(&database.get(&s.as_bytes()).unwrap().unwrap()) + &deserialize::(&state.db.get(&s.as_bytes()).unwrap().unwrap()) .unwrap() .to_string() ); @@ -93,17 +116,30 @@ fn get(path: &str, database: MutexGuard) -> Response { } } -fn post(path: &str, database: MutexGuard) -> Response { - match path { +fn post(request: Request, mut state: MutexGuard) -> Response { + match request.uri.as_str() { // means we wish to create a new thread "/" => { - // first we generate unique id + // first we increment id + let id = state.next_id(); + let content = dbg!(request.form()); + let thread = Thread(vec![ + Post::new(id, None, String::from(*content.get(":D").unwrap())) + ]); + + state.db.insert( + id.to_ne_bytes(), + serialize(&thread) + .unwrap() + ); + + Response::new(Status::Ok, vec![], String::from("")) }, // means we wish to post in specific thread s => { - + Response::new(Status::Ok, vec![], String::from("")) }, } } @@ -112,6 +148,7 @@ fn delete(path: &str, database: Arc>) -> Response { todo!(); } +#[inline] fn content(name: &str, main: &str) -> String { INDEX .replace("{name}", &name) @@ -125,17 +162,21 @@ fn main() { // create threadpool for incoming requests let pool = ThreadPool::new(num_cpus::get()); // open database - let db = Arc::new(Mutex::new(sled::open(&OPT.database).unwrap())); + let state = Arc::new(Mutex::new( + State { + db: sled::open(&OPT.database).unwrap(), + id_counter: 0 + } + )); // TODO setup cache - //let cache = Arc::new(Cache("")); // wait for requests for stream in listener.incoming() { let mut stream = stream.unwrap(); - let database = Arc::clone(&db); + let state = Arc::clone(&state); pool.execute(move || { let ip = stream.peer_addr().unwrap(); - // TODO check if valid ip before reading request + // TODO check if allowed ip before reading request let mut buffer = [0; 1 << 10]; // 2 to the power of 10 // how do I implement images with this? @@ -146,8 +187,7 @@ fn main() { let request: Request = text.parse().unwrap(); // handle request - // add database and cache here - stream.write(handle(request, database, ip).to_string().as_bytes()).unwrap(); + stream.write(handle(request, state).to_string().as_bytes()).unwrap(); }); } } diff --git a/src/post.rs b/src/post.rs index 01dc290..f70dac3 100644 --- a/src/post.rs +++ b/src/post.rs @@ -11,18 +11,18 @@ pub struct Image { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Post { pub id: u64, - pub ip: [u8; 4], - pub date: String, pub img: Option, pub body: String, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Thread(Vec); +pub struct Thread( + pub Vec +); impl Thread { pub fn head(&self) -> String { - let first = self.0[0]; + let first = &self.0[0]; format!( "
{}
{}
", first.id, @@ -32,11 +32,9 @@ impl Thread { } impl Post { - fn new(id: u64, ip: [u8; 4], date: String, img: Option, body: String) -> Self { + pub fn new(id: u64, img: Option, body: String) -> Self { Self { id, - ip, - date, img, body, } @@ -65,8 +63,8 @@ impl fmt::Display for Post { {}\ \ ", - self.id, self.date, self.id, - if let Some(s) = self.img {s.filename} else {"".to_string()}, + self.id, "", self.id, + if let Some(s) = &self.img {&s.filename} else {""}, self.body, ) } diff --git a/src/www/index.html b/src/www/index.html index 0510b7c..4c4b799 100644 --- a/src/www/index.html +++ b/src/www/index.html @@ -3,7 +3,7 @@ kchan - {name} - +
@@ -17,6 +17,14 @@
+
+

Create new thread

+
+ + +
+
+
{}