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.
kchan/src/http.rs

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
}
}