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"
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]]
name = "clap"
version = "2.33.3"
@ -278,9 +291,11 @@ dependencies = [
"bincode",
"cached",
"lazy_static",
"log",
"num_cpus",
"rand",
"serde",
"simplelog",
"sled",
"structopt",
"thiserror",
@ -326,6 +341,25 @@ dependencies = [
"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]]
name = "num_cpus"
version = "1.13.0"
@ -496,6 +530,17 @@ dependencies = [
"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]]
name = "sled"
version = "0.34.7"
@ -565,6 +610,15 @@ dependencies = [
"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]]
name = "textwrap"
version = "0.11.0"
@ -603,6 +657,16 @@ dependencies = [
"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]]
name = "tokio"
version = "1.17.0"
@ -677,6 +741,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"

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

@ -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();
});
}
}

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

Loading…
Cancel
Save