mod xwwwformurlencoded;
mod multipartformdata;
mod multer;
mod bytes_puller;
mod stream_holders;
mod chunked;
use std::borrow::Cow;
use std::collections::HashMap;
pub (crate) use stream_holders::*;
pub use multer::*;
pub use bytes_puller::*;
use std::future::Future;
use std::pin::Pin;
use bytes::Bytes;
pub use multipartformdata::MultiPartFormDataField;
pub use crate::http::request::body::xwwwformurlencoded::*;
pub use chunked::*;
use crate::http::request::header::KeyValueMap;
use crate::server::errors::WaterErrors;
#[derive(Debug)]
pub enum IBody<'a> {
Bytes(&'a [u8]),
MultiPartFormData(FormDataAll),
XWWWFormUrlEncoded(XWWWFormUrlEncoded<'a>)
}
pub enum IBodyChunks<'a> {
Bytes(
BytesPuller<'a>
),
FormData(
MultipartData<'a>
),
#[cfg(feature = "accept_transfer_chunked")]
Chunked(BodyChunkedReader<'a>)
}
pub enum ParsingBodyMechanism {
Default,
JustBytes,
FormData,
XWWWFormData,
#[cfg(feature = "accept_transfer_chunked")]
ChunkedTransferEncoding
}
pub enum ParsingBodyResults<'a> {
Chunked(IBodyChunks<'a>),
FullBody(IBody<'a>),
None,
Err(WaterErrors<'a>)
}
impl <'a> ParsingBodyResults<'a> {
pub async fn on_multipart_form_data_detect(
payload:&'a [u8],
mut on_detect: impl FnMut(
Result<MultiPartFormDataField<'a>,&str>
) -> Pin<Box<dyn Future<Output=HandlingFormDataResult> + Send>>
)->Result<(),&'a str>{
let mut index:usize = 0;
loop {
match MultiPartFormDataField::new(&payload[index..]) {
None => { break;}
Some(data) => {
index = index + data.field_header_length;
let res = on_detect(Ok(data)).await;
match res {
HandlingFormDataResult::Pass => { continue;}
HandlingFormDataResult::Stop => { break; }
HandlingFormDataResult::Shutdown => { return Err("shutdown connection")}
}
}
}
}
Ok(())
}
pub fn is_err(&self)->bool {
if let ParsingBodyResults::Err(_) = self { return true}
false
}
pub fn is_none(&self)->bool {
if let ParsingBodyResults::None = self { return true}
false
}
}
pub enum HandlingFormDataResult {
Pass,
Stop,
Shutdown
}
#[derive(Debug)]
pub struct HeapFormField {
pub multipart:KeyValueMap,
pub data:Vec<u8>
}
impl HeapFormField {
fn from(value: &MultiPartFormDataField,data:&[u8]) -> Self {
let map = KeyValueMap::from(&value.headers);
HeapFormField {
multipart:map,
data:data.to_vec()
}
}
pub fn data(&self)->&[u8]{
self.data.as_ref()
}
pub fn is_file(&self)->bool{
self.multipart.get_as_bytes("filename").is_some()
}
pub fn content_type(&self)->Option<&[u8]>{
if let Some(value) = self.multipart.get_as_bytes("Content-Type") {
return Some(value.as_ref())
}
None
}
}
#[derive(Debug)]
pub struct FormDataAll {
pub fields:Vec<HeapFormField>,
}
impl DynamicBodyMapTrait for FormDataAll {
fn get_as_bytes(&self, key: &str) -> Option<&[u8]> {
if let Some(field) = self.get_field(key) {
return Some(field.data.as_ref())
}
None
}
fn get(&self, key: &str) -> Option<Cow<'_,str>> {
if let Some(data) = self.get_as_bytes(key){
return Some(String::from_utf8_lossy(data))
}
None
}
fn all(&self) -> HashMap<String, Bytes> {
let mut map = HashMap::new();
for field in &self.fields {
for (key,value) in field.multipart.all_pairs() {
map.insert(key.to_string(),value.clone());
}
}
map
}
}
impl FormDataAll {
pub fn get_field(&self,key:&str)->Option<&HeapFormField>{
self.fields.iter().find(|c| {
if let Some(f) = c.multipart.get_as_str("name") {
return f == key || f.replace("\"","" ) == key
}
false
})
}
pub fn get_mut(&mut self,mut key:&str)->Option<&mut HeapFormField>{
if key.starts_with("\"") {
key = &key[1..];
}
if key.ends_with("\""){
key = &key[..key.len()-1];
}
let fields = &mut self.fields;
for field in fields {
if let Some(name) = field.multipart.get_as_str("name") {
if name == key || name.replace("\"","") == key {
return Some(field);
}
}
}
None
}
pub fn new()->FormDataAll{
FormDataAll {
fields:Vec::new(),
}
}
pub (crate) fn push(& mut self,field:&MultiPartFormDataField,data:&[u8]){
if let Some(name) = field.content_disposition_name() {
if let Some( field) = self.get_mut(name.as_ref()) {
field.data.extend_from_slice(data);
return;
}
}
self.fields.push(
HeapFormField::from(field,data)
);
}
}
pub enum HandlingChunkResult<'a> {
Consumed,
Err(&'a str)
}
#[derive(Debug)]
pub enum DynamicBodyMap{
FormField(FormDataAll),
Xww(HeapXWWWFormUrlEncoded)
}
impl DynamicBodyMapTrait for DynamicBodyMap {
fn get_as_bytes(&self, key: &str) -> Option<&[u8]> {
match self {
DynamicBodyMap::FormField(data) => { data.get_as_bytes(key)}
DynamicBodyMap::Xww(data) => {data.get_as_bytes(key)}
}
}
fn get(&self, key: &str) -> Option<Cow<'_,str>> {
match self {
DynamicBodyMap::FormField(data) => { data.get(key)}
DynamicBodyMap::Xww(data) => {data.get(key)}
}
}
fn all(&self) -> HashMap<String, Bytes> {
match self {
DynamicBodyMap::FormField(data) => { data.all()}
DynamicBodyMap::Xww(data) => {data.all()}
}
}
}
pub trait DynamicBodyMapTrait{
fn get_as_bytes(&self,key:&str)-> Option<&[u8]>;
fn get (&self,key:&str)->Option<Cow<'_,str>>;
fn all(&self)-> HashMap<String,Bytes>;
fn get_as_encoded_string(&self, key: &str) -> Option<String> {
let bytes = self.get(key);
if let Some(s) = bytes {
let e = urlencoding::decode(s.as_ref());
if let Ok(c) = e{return Some(c.to_string())}
return Some(s.to_string())
}
None
}
}