|
|
|
@ -1,11 +1,9 @@
|
|
|
|
|
mod errors;
|
|
|
|
|
mod html;
|
|
|
|
|
mod http;
|
|
|
|
|
mod post;
|
|
|
|
|
|
|
|
|
|
use crate::errors::{HandlingError, InternalError, RequestError};
|
|
|
|
|
use crate::html::{FAQ, FAVICON, INDEX, STYLE};
|
|
|
|
|
use crate::http::{Request, Response, Status};
|
|
|
|
|
use crate::errors::{HandlingError, RequestError};
|
|
|
|
|
use crate::http::{Form, Request, Response, Status};
|
|
|
|
|
use crate::post::{Post, Thread};
|
|
|
|
|
|
|
|
|
|
use std::error::Error;
|
|
|
|
@ -31,6 +29,10 @@ use urlencoding::decode;
|
|
|
|
|
|
|
|
|
|
// use this instead of hard-coding
|
|
|
|
|
type ID_TYPE = u32;
|
|
|
|
|
pub const INDEX: &'static str = include_str!("www/index.html");
|
|
|
|
|
pub const FAVICON: &'static [u8] = include_bytes!("www/favicon.ico");
|
|
|
|
|
pub const STYLE: &'static str = include_str!("www/style.css");
|
|
|
|
|
pub const FAQ: &'static str = include_str!("www/faq.html");
|
|
|
|
|
|
|
|
|
|
// represents command line arguments
|
|
|
|
|
#[derive(StructOpt, Debug)]
|
|
|
|
@ -91,7 +93,7 @@ fn get_thread(id: u32) -> Result<Vec<Post>, DatabaseError> {
|
|
|
|
|
|
|
|
|
|
/// generates next id
|
|
|
|
|
fn next_id() -> Result<u32, DatabaseError> {
|
|
|
|
|
DB.fetch_and_update(b"id", increment)
|
|
|
|
|
DB.update_and_fetch(b"id", increment)
|
|
|
|
|
.map(|x| match x {
|
|
|
|
|
Some(s) => {
|
|
|
|
|
let buf: [u8; 4] = (*s).try_into().unwrap();
|
|
|
|
@ -184,7 +186,7 @@ fn handle(mut reader: BufReader<&mut TcpStream>) -> Result<Response, HandlingErr
|
|
|
|
|
if let Some(s) = request
|
|
|
|
|
.headers
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|(a, b)| a.to_lowercase() == "content-length")
|
|
|
|
|
.find(|(a, _)| a.to_lowercase() == "content-length")
|
|
|
|
|
{
|
|
|
|
|
if request.method == Method::Post {
|
|
|
|
|
let body: Vec<u8> = reader
|
|
|
|
@ -197,7 +199,7 @@ fn handle(mut reader: BufReader<&mut TcpStream>) -> Result<Response, HandlingErr
|
|
|
|
|
request.add_body(body);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dbg!(&request);
|
|
|
|
|
//dbg!(&request);
|
|
|
|
|
|
|
|
|
|
match request.method {
|
|
|
|
|
Method::Get => get(&request.uri),
|
|
|
|
@ -224,12 +226,19 @@ fn get(path: &str) -> Result<Response, HandlingError> {
|
|
|
|
|
Ok(Response::new(Status::Ok, vec![], c.into()))
|
|
|
|
|
}
|
|
|
|
|
// TODO favicon.ico
|
|
|
|
|
"/css" => Ok(Response::new(Status::Ok, vec![], String::from(STYLE).into())),
|
|
|
|
|
"/css" => Ok(Response::new(
|
|
|
|
|
Status::Ok,
|
|
|
|
|
vec![],
|
|
|
|
|
String::from(STYLE).into(),
|
|
|
|
|
)),
|
|
|
|
|
"/faq" => Ok(Response::new(Status::Ok, vec![], String::from(FAQ).into())),
|
|
|
|
|
"/favicon.ico" => Ok(Response::new(Status::Ok, vec![("content-type", "image/x-icon")], FAVICON.to_vec())),
|
|
|
|
|
"/favicon.ico" => Ok(Response::new(
|
|
|
|
|
Status::Ok,
|
|
|
|
|
vec![("content-type", "image/x-icon")],
|
|
|
|
|
FAVICON.to_vec(),
|
|
|
|
|
)),
|
|
|
|
|
|
|
|
|
|
// list specific thread here
|
|
|
|
|
// FIXME unwrap hell
|
|
|
|
|
s => {
|
|
|
|
|
let id = s
|
|
|
|
|
.trim_start_matches("/")
|
|
|
|
@ -238,22 +247,35 @@ fn get(path: &str) -> Result<Response, HandlingError> {
|
|
|
|
|
.unwrap()
|
|
|
|
|
.parse::<u32>()
|
|
|
|
|
.map_err(|_| RequestError::NotFound)?;
|
|
|
|
|
let c = get_thread(id)?
|
|
|
|
|
.iter()
|
|
|
|
|
.fold(String::from(""), |a, b| format!("{a}\n{b}"));
|
|
|
|
|
let c = content(&id.to_string(), &c);
|
|
|
|
|
|
|
|
|
|
Ok(Response::new(Status::Ok, vec![], c.into()))
|
|
|
|
|
if s.ends_with("img") {
|
|
|
|
|
Ok(Response::new(
|
|
|
|
|
Status::Ok,
|
|
|
|
|
vec![],
|
|
|
|
|
get_post(id)?.img.ok_or(RequestError::NotFound)?,
|
|
|
|
|
))
|
|
|
|
|
} else {
|
|
|
|
|
let c = get_thread(id)?
|
|
|
|
|
.iter()
|
|
|
|
|
.fold(String::from(""), |a, b| format!("{a}\n{b}"));
|
|
|
|
|
let c = content(&id.to_string(), &c);
|
|
|
|
|
Ok(Response::new(Status::Ok, vec![], c.into()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn post(request: Request) -> Result<Response, HandlingError> {
|
|
|
|
|
let binding = &request.body.ok_or(RequestError::MissingBody)?;
|
|
|
|
|
let c = String::from_utf8_lossy(&binding);
|
|
|
|
|
let c = encode_text(&c).into_owned();
|
|
|
|
|
// TODO check content-type
|
|
|
|
|
|
|
|
|
|
let form = Form::try_from(&request)?;
|
|
|
|
|
|
|
|
|
|
let post = Post::new(0, c);
|
|
|
|
|
let mut post = Post::new(
|
|
|
|
|
0,
|
|
|
|
|
dbg!(encode_text(form.content.trim()))
|
|
|
|
|
.replace("\r\n", "<br>")
|
|
|
|
|
.to_string(),
|
|
|
|
|
);
|
|
|
|
|
post.img = form.image;
|
|
|
|
|
|
|
|
|
|
match request.uri.as_str() {
|
|
|
|
|
// means we wish to create a new thread
|
|
|
|
@ -326,7 +348,7 @@ fn main() {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pool.execute(move || {
|
|
|
|
|
let reader = BufReader::new(&mut stream);
|
|
|
|
|
let reader = BufReader::with_capacity(2 << 20, &mut stream); // 2^20, should be enough for images
|
|
|
|
|
|
|
|
|
|
let response = match handle(reader) {
|
|
|
|
|
Ok(s) => s,
|
|
|
|
|