use std::{collections::HashMap, net::SocketAddr, str};
use serde::de::DeserializeOwned;
use crate::json::InvalidJsonBodyError;
use super::{header::HeaderMap, Method};
const HEADERS_COUNT: usize = 32;
#[derive(Clone, Debug)]
pub struct Request {
method: Method,
path: String,
body: Vec<u8>,
headers: HeaderMap,
peer_addr: Option<SocketAddr>,
pub params: HashMap<String, String>,
}
impl Request {
pub fn builder() -> Builder {
Builder::new()
}
pub fn method(&self) -> Method {
self.method.clone()
}
pub fn path(&self) -> String {
self.path.clone()
}
pub fn headers(&self) -> HeaderMap {
self.headers.clone()
}
pub fn peer_addr(&self) -> Option<SocketAddr> {
self.peer_addr
}
pub fn bytes(&self) -> &Vec<u8> {
&self.body
}
pub fn string(&self) -> Result<String, str::Utf8Error> {
str::from_utf8(&self.body).map(|s| s.to_string())
}
pub fn json<T: DeserializeOwned>(&self) -> Result<T, InvalidJsonBodyError> {
serde_json::from_slice::<T>(&self.body).map_err(|e| e.into())
}
pub fn try_parse_from_bytes(
buffer: &[u8],
peer_addr: Option<SocketAddr>,
) -> Result<Self, ParseRequestError> {
let mut headers = [httparse::EMPTY_HEADER; HEADERS_COUNT];
let mut req = httparse::Request::new(&mut headers);
let mut request = Request::builder().peer_addr(peer_addr);
match req.parse(buffer) {
Ok(httparse::Status::Complete(start_of_body)) => {
let method_str = req.method.ok_or(ParseRequestError::MissingMethod)?;
let path = req.path.ok_or(ParseRequestError::MissingPath)?;
let method = Method::from(method_str);
request = request.method(method).path(path);
for header in req.headers.iter() {
let name = header.name.to_string();
let value = str::from_utf8(header.value)?.to_string();
request = request.header(&name, &value);
}
if let Some(length) = request.headers.get("content-length") {
let length = length.parse::<usize>().unwrap();
let range = &buffer[start_of_body..(start_of_body + length)];
request = request.body(range.to_vec());
}
Ok(request.build())
}
Ok(httparse::Status::Partial) => Err(ParseRequestError::Partial),
Err(e) => Err(e.into()),
}
}
}
#[derive(thiserror::Error, Debug)]
pub enum ParseRequestError {
#[error("method is missing")]
MissingMethod,
#[error("path is missing")]
MissingPath,
#[error("header value is invalid utf-8")]
InvalidUtf8HeaderValue(#[from] str::Utf8Error),
#[error("partial request")]
Partial,
#[error(transparent)]
General(#[from] httparse::Error),
}
pub struct Builder {
method: Method,
path: String,
body: Vec<u8>,
headers: HeaderMap,
peer_addr: Option<SocketAddr>,
params: Option<HashMap<String, String>>,
}
impl Builder {
pub fn new() -> Self {
Builder::default()
}
pub fn method(mut self, method: Method) -> Self {
self.method = method;
self
}
pub fn path(mut self, path: &str) -> Self {
self.path = path.to_string();
self
}
pub fn body(mut self, body: Vec<u8>) -> Self {
self.body = body;
self
}
pub fn header(mut self, key: &str, value: &str) -> Self {
self.headers.insert(key, value);
self
}
pub fn peer_addr(mut self, peer_addr: Option<SocketAddr>) -> Self {
self.peer_addr = peer_addr;
self
}
pub fn build(&self) -> Request {
Request {
peer_addr: self.peer_addr,
method: self.method.clone(),
path: self.path.clone(),
body: self.body.clone(),
headers: self.headers.clone(),
params: Default::default(),
}
}
}
impl Default for Builder {
fn default() -> Self {
Self {
method: Method::Get,
path: "/".to_string(),
body: vec![],
headers: HeaderMap::new(),
params: Default::default(),
peer_addr: None,
}
}
}