use std::fmt; use std::str::{FromStr, Lines}; use crate::errors::{HandlingError, RequestError}; use html_escape::encode_text; const HTTP_VERSION: &'static str = "HTTP/1.1"; #[derive(Debug, Clone)] pub struct Request { pub method: Method, pub uri: String, pub headers: Vec<(String, String)>, pub body: Option>, } #[derive(Debug)] pub struct Response { pub status: Status, pub headers: Vec<(String, String)>, pub body: Vec, // make into Option> } #[derive(Debug, Clone, Copy)] pub enum Status { Ok = 200, SeeOther = 303, NotFound = 404, BadRequest = 400, Unauthorized = 401, InternalServerError = 500, } #[derive(Debug, PartialEq, Clone)] pub enum Method { Get, Post, Delete, } impl FromStr for Method { type Err = RequestError; fn from_str(s: &str) -> Result { match s { "GET" => Ok(Self::Get), "POST" => Ok(Self::Post), "DELETE" => Ok(Self::Delete), _ => Err(Self::Err::UnusedMethod), } } } impl Status { pub fn message(&self) -> &'static str { match self { Self::Ok => "OK", Self::NotFound => "NOT FOUND", Self::BadRequest => "BAD REQUEST", Self::Unauthorized => "UNAUTHORIZED", Self::InternalServerError => "INTERNAL SERVER ERROR", Self::SeeOther => "SEE OTHER", } } } #[derive(Debug)] pub struct Form { pub content: String, pub image: Option>, } // TODO do this more imperatively, fuck this complicated splitting shit impl TryFrom<&Request> for Form { type Error = RequestError; fn try_from(value: &Request) -> Result { // check if body let body = value.body.as_ref().ok_or(RequestError::MissingBody)?; // get boundary let mut boundary: Vec = vec![]; boundary.extend_from_slice(b"--"); boundary.extend( value .headers .iter() .find(|(a, _)| a.to_lowercase() == "content-type") .map(|(_, b)| b) .ok_or(RequestError::NotAForm)? .chars() .skip_while(|&c| c != '=') // NOTE hope this means boundary .skip(1) .map(|c| c as u8), ); //boundary.extend_from_slice(b"\r\n"); //boundary.extend_from_slice(b"\n"); // setup basic stuff let sep = b"\r\n\r\n"; let mut pos = 0; // get content pos += body .windows(boundary.len()) .position(|x| x == boundary) .ok_or(RequestError::NotAForm)? + boundary.len(); // skip useless shit and get to the point pos += body[pos..] .windows(sep.len()) .position(|x| x == sep) .ok_or(RequestError::NotAForm)? + sep.len(); let temp = body[pos..] .windows(boundary.len()) .position(|x| x == boundary) .ok_or(RequestError::NotAForm)? + pos; let content = String::from_utf8_lossy(&body[pos..temp]).to_string(); //let content = encode_text(&content.trim().to_string()).to_string(); // do that later dbg!(&content); // good so far pos = temp + boundary.len(); pos += body[pos..] .windows(sep.len()) .position(|x| x == sep) .ok_or(RequestError::NotAForm)? + sep.len(); // now at image data let temp = body[pos..] .windows(boundary.len()) .position(|x| x == boundary) .ok_or(RequestError::NotAForm)? + pos; let image = if &body[pos..temp] == b"\r\n" { dbg!("no image"); None } else { Some(body[pos..temp].to_owned()) }; Ok(Form { content, image }) } } impl Request { pub fn add_body(&mut self, body: Vec) { self.body = Some(body); } // pub fn form(&self) -> Result, RequestError> { // let mut hashmap = HashMap::new(); // dbg!(&self.body); // for mut x in self.body.split("&").map(|x| x.split("=")) { // hashmap.insert( // x.next().ok_or(RequestError::NotAForm)?, // decode(x.next().ok_or(RequestError::NotAForm)?)?.into_owned().replace("+", " "), // ); // } // Ok(hashmap) // } } impl TryFrom> for Request { type Error = RequestError; fn try_from(s: Vec) -> Result { //dbg!(&s); let mut iter = s.iter(); let mut first = iter .next() .ok_or(RequestError::BadRequest)? .split_whitespace(); let method: Method = first.next().ok_or(RequestError::BadRequest)?.parse()?; let uri = first.next().ok_or(RequestError::BadRequest)?.to_string(); let mut headers: Vec<(String, String)> = vec![]; for line in iter { let mut line = line.split(":").take(2); let left = line.next().ok_or(RequestError::BadRequest)?.trim(); let right = line.next().ok_or(RequestError::BadRequest)?.trim(); headers.push((left.to_string(), right.to_string())); } Ok(Self { method, uri, headers, body: None, }) } } impl Response { pub fn new(status: Status, headers: Vec<(&str, &str)>, body: Vec) -> Self { Self { status, headers: headers .into_iter() .map(|(x, y)| (x.to_owned(), y.to_owned())) .collect(), body, } } pub fn respond(&self) -> Vec { let mut res = vec![]; res.extend_from_slice(HTTP_VERSION.as_bytes()); res.push(b' '); res.extend_from_slice(&(self.status as i32).to_string().as_bytes()); // network endianness res.push(b' '); res.extend_from_slice(self.status.message().as_bytes()); res.extend_from_slice(b"\r\n"); for (i, j) in self.headers.iter() { res.extend_from_slice(i.as_bytes()); res.push(b':'); res.extend_from_slice(j.as_bytes()); res.extend_from_slice(b"\r\n"); } res.extend_from_slice(b"\r\n"); res.extend_from_slice(&self.body); res } }