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.
doctorpavel
Dawid J. Kubis 3 years ago
parent d8761eee20
commit 3a738ba31f

@ -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<Self, Self::Err> {
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 {
)
}
}

@ -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<Mutex<sled::Db>>, ip: SocketAddr) -> Response {
let database = database.lock().unwrap();
fn handle(request: Request, state: Arc<Mutex<State>>) -> 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<Mutex<sled::Db>>, ip: SocketAddr) -> R
/// get "/" returns head of every thread
/// or returns posts from thread with id in uri
fn get(path: &str, database: MutexGuard<sled::Db>) -> Response {
fn get(path: &str, state: MutexGuard<State>) -> 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::<Thread>(&x.1).unwrap()))
@ -78,12 +95,18 @@ fn get(path: &str, database: MutexGuard<sled::Db>) -> 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::<Thread>(&database.get(&s.as_bytes()).unwrap().unwrap())
&deserialize::<Thread>(&state.db.get(&s.as_bytes()).unwrap().unwrap())
.unwrap()
.to_string()
);
@ -93,17 +116,30 @@ fn get(path: &str, database: MutexGuard<sled::Db>) -> Response {
}
}
fn post(path: &str, database: MutexGuard<sled::Db>) -> Response {
match path {
fn post(request: Request, mut state: MutexGuard<State>) -> 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<Mutex<sled::Db>>) -> 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();
});
}
}

@ -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<Image>,
pub body: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thread(Vec<Post>);
pub struct Thread(
pub Vec<Post>
);
impl Thread {
pub fn head(&self) -> String {
let first = self.0[0];
let first = &self.0[0];
format!(
"<article><div><span class=\"info\">{}</span></div>{}</article>",
first.id,
@ -32,11 +32,9 @@ impl Thread {
}
impl Post {
fn new(id: u64, ip: [u8; 4], date: String, img: Option<Image>, body: String) -> Self {
pub fn new(id: u64, img: Option<Image>, body: String) -> Self {
Self {
id,
ip,
date,
img,
body,
}
@ -65,8 +63,8 @@ impl fmt::Display for Post {
{}\
</div>\
</article>",
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,
)
}

@ -3,7 +3,7 @@
<head>
<title>kchan - {name}</title>
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="css">
</head>
<body>
<header>
@ -17,6 +17,14 @@
</div>
</header>
<main>
<div class="wrap slim">
<h1>Create new thread</h1>
<form method="post">
<textarea name=":D" rows="10" placeholder="Content" required></textarea>
<input type="submit" value="Create">
</form>
</div>
<hr>
<div class="wrap">
{}
</div>

Loading…
Cancel
Save