You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
5.3 KiB
Rust
231 lines
5.3 KiB
Rust
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<Vec<u8>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Response {
|
|
pub status: Status,
|
|
pub headers: Vec<(String, String)>,
|
|
pub body: Vec<u8>, // make into Option<Vec<u8>>
|
|
}
|
|
|
|
#[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<Self, Self::Err> {
|
|
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<Vec<u8>>,
|
|
}
|
|
|
|
// TODO do this more imperatively, fuck this complicated splitting shit
|
|
impl TryFrom<&Request> for Form {
|
|
type Error = RequestError;
|
|
|
|
fn try_from(value: &Request) -> Result<Self, Self::Error> {
|
|
// check if body
|
|
let body = value.body.as_ref().ok_or(RequestError::MissingBody)?;
|
|
|
|
// get boundary
|
|
|
|
let mut boundary: Vec<u8> = 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<u8>) {
|
|
self.body = Some(body);
|
|
}
|
|
// pub fn form(&self) -> Result<HashMap<&str, String>, 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<Vec<String>> for Request {
|
|
type Error = RequestError;
|
|
|
|
fn try_from(s: Vec<String>) -> Result<Self, Self::Error> {
|
|
//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<u8>) -> Self {
|
|
Self {
|
|
status,
|
|
headers: headers
|
|
.into_iter()
|
|
.map(|(x, y)| (x.to_owned(), y.to_owned()))
|
|
.collect(),
|
|
body,
|
|
}
|
|
}
|
|
|
|
pub fn respond(&self) -> Vec<u8> {
|
|
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
|
|
}
|
|
}
|