use super::Request;
use bytes::Bytes;
#[cfg(feature = "parsers")]
use reinhardt_core::parsers::parser::{ParsedData, Parser};
#[cfg(feature = "parsers")]
use std::collections::HashMap;
use std::sync::atomic::Ordering;
impl Request {
pub fn body(&self) -> &Bytes {
&self.body
}
pub fn json<T: serde::de::DeserializeOwned>(&self) -> crate::Result<T> {
use crate::Error;
if let Some(content_type) = self
.headers
.get(hyper::header::CONTENT_TYPE)
.and_then(|h| h.to_str().ok())
{
if !content_type.starts_with("application/json") {
return Err(Error::Http(format!(
"Unsupported Media Type: expected 'application/json', got '{}'",
content_type
)));
}
} else if !self.body.is_empty() {
return Err(Error::Http(
"Missing Content-Type header: expected 'application/json'".to_string(),
));
}
serde_json::from_slice(&self.body).map_err(|e| Error::Serialization(e.to_string()))
}
#[cfg(feature = "parsers")]
pub fn with_parsers(mut self, parsers: Vec<Box<dyn Parser>>) -> Self {
self.parsers = parsers;
self
}
pub fn read_body(&self) -> crate::Result<Bytes> {
use crate::Error;
if self.body_consumed.load(Ordering::SeqCst) {
return Err(Error::Http(
"Request body has already been consumed".to_string(),
));
}
self.body_consumed.store(true, Ordering::SeqCst);
Ok(self.body.clone())
}
#[cfg(feature = "parsers")]
pub async fn post(&self) -> crate::Result<HashMap<String, Vec<String>>> {
use crate::Error;
if self.body_consumed.load(Ordering::SeqCst) {
return Err(Error::Http(
"Request body has already been consumed".to_string(),
));
}
let has_form_parser = self.parsers.iter().any(|p| {
let media_types = p.media_types();
media_types.contains(&"application/x-www-form-urlencoded".to_string())
|| media_types.contains(&"multipart/form-data".to_string())
});
if !has_form_parser {
return Ok(HashMap::new());
}
let parsed = self.parse_body_internal().await?;
match parsed {
ParsedData::Form(form) => {
Ok(form.into_iter().map(|(k, v)| (k, vec![v])).collect())
}
ParsedData::MultiPart { fields, .. } => {
Ok(fields.into_iter().map(|(k, v)| (k, vec![v])).collect())
}
_ => Ok(HashMap::new()),
}
}
#[cfg(feature = "parsers")]
pub async fn data(&self) -> crate::Result<ParsedData> {
use crate::Error;
if self.body_consumed.load(Ordering::SeqCst) {
return Err(Error::Http(
"Request body has already been consumed".to_string(),
));
}
self.parse_body_internal().await
}
#[cfg(feature = "parsers")]
pub(super) async fn parse_body_internal(&self) -> crate::Result<ParsedData> {
{
let cache = self.parsed_data.lock().unwrap();
if let Some(data) = &*cache {
return Ok(data.clone());
}
}
let content_type = self
.headers
.get(hyper::header::CONTENT_TYPE)
.and_then(|h| h.to_str().ok());
for parser in &self.parsers {
if parser.can_parse(content_type) {
match parser
.parse(content_type, self.body.clone(), &self.headers)
.await
{
Ok(data) => {
let mut cache = self.parsed_data.lock().unwrap();
*cache = Some(data.clone());
return Ok(data);
}
Err(e) => {
use crate::Error;
return Err(Error::Http(format!("Parse error: {}", e)));
}
}
}
}
use crate::Error;
Err(Error::Http(
"No suitable parser found for content type".to_string(),
))
}
}