use cookie::Cookie;
use header::CONTENT_TYPE;
use mime::{APPLICATION_WWW_FORM_URLENCODED, Mime};
use serde::{Deserialize, de::DeserializeOwned};
use hyper::{body::Bytes, header};
use hyper::http::request::Parts as ReqParts;
use std::collections::HashMap;
use crate::{Error, security};
type HyperRequest = hyper::Request<hyper::Body>;
pub struct Body {
body: hyper::Body,
content_type: Option<header::HeaderValue>,
}
pub fn adapt<'a>(req: HyperRequest) -> (Parts, Body) {
let (parts, body) = req.into_parts();
let body = Body{body, content_type: parts.headers.get(CONTENT_TYPE).map(|x| x.to_owned())};
let parts = Parts{parts, cookies: None};
(parts, body)
}
pub struct Parts {
parts: ReqParts,
cookies: Option<HashMap<String,Cookie<'static>>>,
}
#[derive(Deserialize)]
struct CsrfData {
csrf: String,
}
impl Parts {
pub fn cookies(&mut self) -> &HashMap<String,Cookie> {
if let Some(ref cookies) = self.cookies {
return cookies
}
let mut cookies = HashMap::new();
for header in self.parts.headers.get_all(header::COOKIE) {
let raw_str = match std::str::from_utf8(header.as_bytes()) {
Ok(string) => string,
Err(_) => continue
};
for cookie_str in raw_str.split(';').map(|s| s.trim()) {
if let Ok(cookie) = Cookie::parse_encoded(cookie_str) {
cookies.insert(cookie.name().to_string(), cookie.into_owned());
}
}
}
self.cookies = Some(cookies);
&self.cookies.as_ref().unwrap()
}
pub fn method(&self) -> &hyper::Method {
&self.parts.method
}
pub fn headers(&self) -> &hyper::HeaderMap<header::HeaderValue> {
&self.parts.headers
}
pub fn uri(&self) -> &hyper::Uri {
&self.parts.uri
}
pub fn query<T: DeserializeOwned>(&self) -> Result<T,Error> {
serde_urlencoded::from_str::<T>(self.parts.uri.query().unwrap_or("")).map_err(|e|Error::bad_request(e.to_string()))
}
}
impl Body {
pub async fn into_bytes(self) -> Result<Bytes,Error> {
hyper::body::to_bytes(self.body).await.map_err(|_|Error::internal("failed to read body".to_string()))
}
pub async fn into_form<T: DeserializeOwned>(self) -> Result<T,Error> {
self.enforce_content_type(APPLICATION_WWW_FORM_URLENCODED)?;
let full_body = self.into_bytes().await?;
serde_urlencoded::from_bytes::<T>(&full_body).map_err(|e|Error::bad_request(e.to_string()))
}
pub async fn into_form_csrf<T: DeserializeOwned>(self, csrf_token: &security::CsrfToken) -> Result<T,Error> {
self.enforce_content_type(APPLICATION_WWW_FORM_URLENCODED)?;
let full_body = self.into_bytes().await?;
let csrf_data = serde_urlencoded::from_bytes::<CsrfData>(&full_body).map_err(|_|Error::bad_request("no csrf token".to_string()))?;
csrf_token.matches(csrf_data.csrf)?;
serde_urlencoded::from_bytes::<T>(&full_body).map_err(|e|Error::bad_request(e.to_string()))
}
fn enforce_content_type(&self, mime: Mime) -> Result<(),Error> {
if let Some(content_type) = &self.content_type {
if *content_type == mime.to_string() {
return Ok(())
}
}
Err(Error::bad_request(format!("expected content-type: {}", mime)))
}
}