|
|
|
@ -1,11 +1,13 @@
|
|
|
|
|
mod errors;
|
|
|
|
|
mod http;
|
|
|
|
|
mod post;
|
|
|
|
|
|
|
|
|
|
use crate::errors::RequestError;
|
|
|
|
|
use crate::http::{Request, Response, Status};
|
|
|
|
|
use crate::post::{Post, Thread};
|
|
|
|
|
|
|
|
|
|
use std::io::{Read, Write};
|
|
|
|
|
use std::net::{TcpListener, TcpStream, SocketAddr};
|
|
|
|
|
use std::net::{SocketAddr, TcpListener, TcpStream};
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::str;
|
|
|
|
|
use std::str::from_utf8;
|
|
|
|
@ -32,17 +34,14 @@ struct Opt {
|
|
|
|
|
#[structopt(short, long)]
|
|
|
|
|
admin_hash: Option<String>,
|
|
|
|
|
|
|
|
|
|
#[structopt(short, long, default_value = "data.sled")]
|
|
|
|
|
#[structopt(short, long, default_value = "data")]
|
|
|
|
|
database: PathBuf,
|
|
|
|
|
|
|
|
|
|
#[structopt(short, long, default_value = "10000")]
|
|
|
|
|
max_posts: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get command line arguments and make them static
|
|
|
|
|
// safe because they're read-only
|
|
|
|
|
lazy_static! {
|
|
|
|
|
// first parse command line arguments
|
|
|
|
|
// parse command line arguments
|
|
|
|
|
static ref OPT: Opt = Opt::from_args();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -66,10 +65,11 @@ fn handle(request: Request, state: Arc<Mutex<State>>) -> Response {
|
|
|
|
|
let state = state.lock().unwrap();
|
|
|
|
|
|
|
|
|
|
match request.method.as_str() {
|
|
|
|
|
"GET" => get(&request.uri, state),
|
|
|
|
|
"POST" => {
|
|
|
|
|
post(request, state)
|
|
|
|
|
"GET" => match get(&request.uri, state) {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => Response::new(Status::NotFound, vec![], "".to_string()),
|
|
|
|
|
},
|
|
|
|
|
"POST" => post(request, state),
|
|
|
|
|
// check admin hash
|
|
|
|
|
"DELETE" => Response::new(Status::Ok, vec![], "".to_string()),
|
|
|
|
|
_ => Response::new(Status::Ok, vec![], "".to_string()),
|
|
|
|
@ -78,7 +78,7 @@ fn handle(request: Request, state: Arc<Mutex<State>>) -> Response {
|
|
|
|
|
|
|
|
|
|
/// get "/" returns head of every thread
|
|
|
|
|
/// or returns posts from thread with id in uri
|
|
|
|
|
fn get(path: &str, state: MutexGuard<State>) -> Response {
|
|
|
|
|
fn get(path: &str, state: MutexGuard<State>) -> Result<Response, HandlingError> {
|
|
|
|
|
match path {
|
|
|
|
|
// list threads in this case
|
|
|
|
|
"/" => {
|
|
|
|
@ -93,13 +93,12 @@ fn get(path: &str, state: MutexGuard<State>) -> Response {
|
|
|
|
|
.fold(String::from(""), |a, b| format!("{}{}", a, b)),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Response::new(Status::Ok, vec![], content)
|
|
|
|
|
Ok(Response::new(Status::Ok, vec![], content))
|
|
|
|
|
}
|
|
|
|
|
// TODO /css /faq /favicon.ico
|
|
|
|
|
"/css" => Ok(Response::new(Status::Ok, vec![], String::from(STYLE))),
|
|
|
|
|
|
|
|
|
|
"/css" => Response::new(Status::Ok, vec![], String::from(STYLE)),
|
|
|
|
|
|
|
|
|
|
"/faq" => Response::new(Status::Ok, vec![], String::from(FAQ)),
|
|
|
|
|
"/faq" => Ok(Response::new(Status::Ok, vec![], String::from(FAQ))),
|
|
|
|
|
|
|
|
|
|
// list specific thread here
|
|
|
|
|
// FIXME unwrap hell
|
|
|
|
@ -108,39 +107,36 @@ fn get(path: &str, state: MutexGuard<State>) -> Response {
|
|
|
|
|
s,
|
|
|
|
|
&deserialize::<Thread>(&state.db.get(&s.as_bytes()).unwrap().unwrap())
|
|
|
|
|
.unwrap()
|
|
|
|
|
.to_string()
|
|
|
|
|
.to_string(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Response::new(Status::Ok, vec![], content)
|
|
|
|
|
},
|
|
|
|
|
Ok(Response::new(Status::Ok, vec![], content))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn post(request: Request, mut state: MutexGuard<State>) -> Response {
|
|
|
|
|
match request.uri.as_str() {
|
|
|
|
|
|
|
|
|
|
// means we wish to create a new thread
|
|
|
|
|
"/" => {
|
|
|
|
|
// 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()))
|
|
|
|
|
]);
|
|
|
|
|
let thread = Thread(vec![Post::new(
|
|
|
|
|
id,
|
|
|
|
|
None,
|
|
|
|
|
String::from(*content.get(":D").unwrap()),
|
|
|
|
|
)]);
|
|
|
|
|
|
|
|
|
|
state.db.insert(
|
|
|
|
|
id.to_ne_bytes(),
|
|
|
|
|
serialize(&thread)
|
|
|
|
|
.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(""))
|
|
|
|
|
},
|
|
|
|
|
s => Response::new(Status::Ok, vec![], String::from("")),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -150,29 +146,33 @@ fn delete(path: &str, database: Arc<Mutex<sled::Db>>) -> Response {
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn content(name: &str, main: &str) -> String {
|
|
|
|
|
INDEX
|
|
|
|
|
.replace("{name}", &name)
|
|
|
|
|
.replace("{}", &main)
|
|
|
|
|
INDEX.replace("{name}", &name).replace("{}", &main)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
|
// TODO do this without unwrap
|
|
|
|
|
// bind listener to local adress and port
|
|
|
|
|
let listener = TcpListener::bind(("127.0.0.1", OPT.port)).unwrap();
|
|
|
|
|
let listener =
|
|
|
|
|
TcpListener::bind(("127.0.0.1", OPT.port)).expect("Cannot bind to specified port.");
|
|
|
|
|
// create threadpool for incoming requests
|
|
|
|
|
let pool = ThreadPool::new(num_cpus::get());
|
|
|
|
|
// setup logger
|
|
|
|
|
|
|
|
|
|
// open database
|
|
|
|
|
let state = Arc::new(Mutex::new(
|
|
|
|
|
State {
|
|
|
|
|
let state = Arc::new(Mutex::new(State {
|
|
|
|
|
db: sled::open(&OPT.database).unwrap(),
|
|
|
|
|
id_counter: 0
|
|
|
|
|
}
|
|
|
|
|
));
|
|
|
|
|
id_counter: 0,
|
|
|
|
|
}));
|
|
|
|
|
// TODO setup cache
|
|
|
|
|
|
|
|
|
|
// wait for requests
|
|
|
|
|
for stream in listener.incoming() {
|
|
|
|
|
let mut stream = stream.unwrap();
|
|
|
|
|
let mut stream = match stream {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
eprintln!("failed connection: {}", e);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
let state = Arc::clone(&state);
|
|
|
|
|
pool.execute(move || {
|
|
|
|
|
let ip = stream.peer_addr().unwrap();
|
|
|
|
@ -187,7 +187,9 @@ fn main() {
|
|
|
|
|
let request: Request = text.parse().unwrap();
|
|
|
|
|
|
|
|
|
|
// handle request
|
|
|
|
|
stream.write(handle(request, state).to_string().as_bytes()).unwrap();
|
|
|
|
|
stream
|
|
|
|
|
.write(handle(request, state).to_string().as_bytes())
|
|
|
|
|
.unwrap();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|