first commit in train, added filled errors.rs, working on error handling

doctorpavel
Dawid J. Kubis 3 years ago
parent 3a738ba31f
commit f8fa5df49b

73
Cargo.lock generated

@ -108,6 +108,19 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time",
"winapi",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.3" version = "2.33.3"
@ -278,9 +291,11 @@ dependencies = [
"bincode", "bincode",
"cached", "cached",
"lazy_static", "lazy_static",
"log",
"num_cpus", "num_cpus",
"rand", "rand",
"serde", "serde",
"simplelog",
"sled", "sled",
"structopt", "structopt",
"thiserror", "thiserror",
@ -326,6 +341,25 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num-integer"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.13.0"
@ -496,6 +530,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "simplelog"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1348164456f72ca0116e4538bdaabb0ddb622c7d9f16387c725af3e96d6001c"
dependencies = [
"chrono",
"log",
"termcolor",
]
[[package]] [[package]]
name = "sled" name = "sled"
version = "0.34.7" version = "0.34.7"
@ -565,6 +610,15 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.11.0" version = "0.11.0"
@ -603,6 +657,16 @@ dependencies = [
"num_cpus", "num_cpus",
] ]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi",
]
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.17.0" version = "1.17.0"
@ -677,6 +741,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

@ -16,3 +16,6 @@ sled = "0.34.7"
bincode = "1.3.3" bincode = "1.3.3"
serde = { version = "1.0.136", features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] }
cached = "0.33.0" cached = "0.33.0"
log = "0.4.14"
simplelog = "0.11.2"

@ -0,0 +1,25 @@
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RequestError {
#[error("request unable to be parsed as a form")]
NotAForm,
#[error("request not authorized, incorred hash: {0}")]
NotAuthorized(String),
}
#[derive(Debug, Error)]
pub enum InternalError {
#[error("failed to read from database")]
DatabaseReadError,
#[error("failed to write to database")]
DatabaseWriteError,
}
#[derive(Debug, Error)]
pub enum HandlingError {
#[error("client error")]
ClientError(#[from] RequestError),
#[error("server error")]
ServerError(#[from] InternalError),
}

@ -2,6 +2,8 @@ use std::collections::HashMap;
use std::fmt; use std::fmt;
use std::str::{FromStr, Lines}; use std::str::{FromStr, Lines};
use crate::errors::RequestError;
const HTTP_VERSION: &'static str = "HTTP/1.1"; const HTTP_VERSION: &'static str = "HTTP/1.1";
#[derive(Debug)] #[derive(Debug)]
@ -23,6 +25,9 @@ pub struct Response {
pub enum Status { pub enum Status {
Ok = 200, Ok = 200,
NotFound = 404, NotFound = 404,
BadRequest = 400,
Unauthorized = 401,
InternalServerError = 500,
} }
impl Status { impl Status {
@ -30,20 +35,22 @@ impl Status {
match self { match self {
Self::Ok => "OK", Self::Ok => "OK",
Self::NotFound => "NOT FOUND", Self::NotFound => "NOT FOUND",
Self::BadRequest => "BAD REQUEST",
Self::Unauthorized => "UNAUTHORIZED",
Self::InternalServerError => "INTERNAL SERVER ERROR",
} }
} }
} }
impl Request { impl Request {
pub fn form(&self) -> HashMap<&str, &str> { pub fn form(&self) -> Result<HashMap<&str, &str>, RequestError> {
let mut hashmap = HashMap::new(); let mut hashmap = HashMap::new();
self.body.split("&") self.body.split("&").map(|x| x.split("=")).for_each(
.map(|x| x.split("=")) |mut x| {
.for_each( hashmap.insert(x.next().unwrap(), x.next().unwrap());
|mut x| {hashmap.insert(x.next().unwrap(), x.next().unwrap());} }, // fix unwrap hell
// fix unwrap hell
); );
hashmap Ok(hashmap)
} }
} }
@ -106,4 +113,3 @@ impl fmt::Display for Response {
) )
} }
} }

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

@ -16,9 +16,7 @@ pub struct Post {
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thread( pub struct Thread(pub Vec<Post>);
pub Vec<Post>
);
impl Thread { impl Thread {
pub fn head(&self) -> String { pub fn head(&self) -> String {
@ -33,11 +31,7 @@ impl Thread {
impl Post { impl Post {
pub fn new(id: u64, img: Option<Image>, body: String) -> Self { pub fn new(id: u64, img: Option<Image>, body: String) -> Self {
Self { Self { id, img, body }
id,
img,
body,
}
} }
} }
@ -63,8 +57,14 @@ impl fmt::Display for Post {
{}\ {}\
</div>\ </div>\
</article>", </article>",
self.id, "", self.id, self.id,
if let Some(s) = &self.img {&s.filename} else {""}, "",
self.id,
if let Some(s) = &self.img {
&s.filename
} else {
""
},
self.body, self.body,
) )
} }
@ -79,4 +79,3 @@ impl fmt::Display for Thread {
write!(f, "{}", posts) write!(f, "{}", posts)
} }
} }

Loading…
Cancel
Save