racoon 0.1.9

Racoon is a fast, fully customizable web framework for Rust focusing on simplicity.
Documentation
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

use tokio::sync::Mutex;

use crate::core::forms::{Files, FormConstraints, FormData};

use crate::core::headers::{HeaderValue, Headers};
use crate::core::parser::multipart::MultipartParser;
use crate::core::parser::urlencoded::UrlEncodedParser;
use crate::core::server::Context;
use crate::core::stream::Stream;

use crate::core::path::PathParams;
use crate::{racoon_debug, racoon_error};

use crate::core::cookie::{parse_cookies_from_header, Cookies};
use crate::core::session::{Session, SessionManager};
use crate::core::shortcuts::SingleText;

use super::forms::FormFieldError;

pub type QueryParams = HashMap<String, Vec<String>>;

pub struct Request {
    pub stream: Arc<Stream>,
    context: Arc<Context>,
    pub scheme: String,
    pub method: String,
    pub path: String,
    pub http_version: u8,
    pub headers: Headers,
    pub path_params: PathParams,
    pub query_params: QueryParams,
    pub cookies: Cookies,
    pub session: Session,
    pub body_read: Arc<AtomicBool>,
    pub form_constraints: Arc<FormConstraints>,
    pub response_headers: Arc<Mutex<Headers>>,
}

impl Request {
    pub async fn from(
        stream: Arc<Stream>,
        context: Arc<Context>,
        scheme: String,
        method: String,
        path: String,
        http_version: u8,
        headers: Headers,
        path_params: PathParams,
        query_params: QueryParams,
        session_manager: Arc<SessionManager>,
        body_read: Arc<AtomicBool>,
        form_constraints: Arc<FormConstraints>,
        response_headers: Arc<Mutex<Headers>>,
    ) -> Self {
        let cookies = parse_cookies_from_header(&headers);
        let session_id = cookies.value("sessionid");

        let session = Session::from(session_manager, session_id, response_headers.clone());

        Self {
            stream,
            context,
            scheme,
            method,
            path,
            http_version,
            headers,
            path_params,
            query_params,
            cookies,
            session,
            body_read,
            form_constraints,
            response_headers,
        }
    }

    pub async fn remote_addr(&self) -> Option<SocketAddr> {
        self.stream.peer_addr().await
    }

    pub fn context<T: 'static>(&self) -> Option<&T> {
        self.context.downcast_ref::<T>()
    }

    pub async fn parse(&self) -> (FormData, Files) {
        return match self.parse_body(self.form_constraints.clone()).await {
            Ok((form_data, files)) => (form_data, files),
            Err(_) => (FormData::new(), Files::new()),
        };
    }

    pub async fn parse_body(
        &self,
        form_constraints: Arc<FormConstraints>,
    ) -> Result<(FormData, Files), FormFieldError> {
        let form_data = FormData::new();
        let files = Files::new();

        let content_type;
        if let Some(value) = self.headers.value("Content-Type") {
            content_type = value;
        } else {
            racoon_debug!("Content type is missing.");
            return Ok((form_data, files));
        }

        let body_read = self.body_read.clone();
        body_read.store(false, Ordering::Relaxed);

        if content_type
            .to_lowercase()
            .starts_with("multipart/form-data")
        {
            racoon_debug!("Parsing with MultipartParser");

            return match MultipartParser::parse(
                self.stream.clone(),
                form_constraints,
                &self.headers,
            )
            .await
            {
                Ok((form_data, files)) => {
                    self.body_read.store(true, Ordering::Relaxed);
                    Ok((form_data, files))
                }
                Err(error) => {
                    racoon_error!("Error while parsing multipart body: {:?}", error);
                    Err(error)
                }
            };
        } else if content_type
            .to_lowercase()
            .starts_with("application/x-www-form-urlencoded")
        {
            racoon_debug!("Parsing with UrlEncoded parser.");

            return match UrlEncodedParser::parse(
                self.stream.clone(),
                &self.headers,
                form_constraints,
            )
            .await
            {
                Ok(form_data) => {
                    self.body_read.store(true, Ordering::Relaxed);
                    Ok((form_data, files))
                }
                Err(error) => {
                    racoon_error!("Error while parsing x-www-urlencoded form. {:?}", error);
                    Err(error)
                }
            };
        }

        racoon_debug!("Unhandled enctype: {}", content_type);
        Ok((form_data, files))
    }
}

impl Clone for Request {
    fn clone(&self) -> Self {
        Self {
            stream: self.stream.clone(),
            context: self.context.clone(),
            scheme: self.scheme.clone(),
            method: self.method.clone(),
            path: self.path.clone(),
            http_version: self.http_version.clone(),
            headers: self.headers.clone(),
            path_params: self.path_params.clone(),
            query_params: self.query_params.clone(),
            cookies: self.cookies.clone(),
            session: self.session.clone(),
            body_read: self.body_read.clone(),
            form_constraints: self.form_constraints.clone(),
            response_headers: self.response_headers.clone(),
        }
    }
}

#[derive(Debug)]
pub enum RequestError {
    HeaderSizeExceed,
    Others(String),
}