tina-core 0.0.2

Tina platform
Documentation
//! Request转换

use crate::tina::data::app_error::AppError;
use crate::tina::data::http::multipart::send::de::MultipartDeserializer;
use crate::tina::data::http::multipart::send::Multipart;
use crate::tina::data::http::{
    request_json::HttpReqJson, request_metadata::HttpReqMetadata, request_multipart::HttpReqMultipart, request_param::HttpReqParam,
    request_path::HttpReqPath,
};
use crate::tina::data::json::ToJson;
use crate::tina::data::validate::Validated;
use crate::tina::data::AppResult;
use crate::tina::i18n::message::system_message::SystemMessage;
use crate::tina::server::application::Application;
use crate::tina::server::http::multipart::MultipartData;
use crate::tina::server::http::request::RequestExt;
use crate::tina::server::session::Session;
use crate::{app_error_from, i18n_string};
use axum::extract::{FromRequest, FromRequestParts, Path};
use axum::BoxError;
use bytes::{Buf, Bytes};
use http::header::CONTENT_LENGTH;
use http::request::Parts;
use http::Request;
use http_body::Body;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::{borrow::Cow, fmt::Debug};

use super::middleware::request_init_handler::copy_req_parts;
use super::request_ext::HttpRequestAttribute2;

fn url_decode(param_str: &str) -> AppResult<String> {
    match urlencoding::decode(param_str) {
        Ok(s) => Ok(s.into_owned()),
        Err(e) => Err(crate::app_param_check_error_with_msg!(i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_FORMAT), "{:?}", e)),
    }
}

/// 通过自定义
pub struct ViaCustom;
#[async_trait]
impl<S> FromRequestParts<S> for ViaCustom {
    type Rejection = AppError;

    async fn from_request_parts(_req: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        Ok(ViaCustom)
    }
}

#[async_trait]
impl<S> FromRequestParts<S> for Application
where
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let application = match parts.get_application() {
            Ok(app) => app,
            Err(err) => {
                return Err(err);
            }
        };
        Ok(application)
    }
}

#[async_trait]
impl<S> FromRequestParts<S> for Session
where
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let session = Session::from_http_request(parts).await?;
        Ok(session)
    }
}

#[async_trait]
impl<S> FromRequestParts<S> for HttpReqMetadata
where
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request_parts(req: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let application = match req.get_application() {
            Ok(app) => app,
            Err(err) => {
                return Err(err);
            }
        };
        let session = Session::from_http_request(req).await?;
        let req2 = copy_req_parts(req)?;
        Ok(HttpReqMetadata::new(req2, application, session))
    }
}

#[async_trait]
impl<D, S, B> FromRequest<S, B> for HttpReqJson<D>
where
    D: DeserializeOwned + Serialize + Validated + Send + Sync + 'static,
    B: Body + Debug + Unpin + Send + Sync + 'static,
    B::Data: Send,
    B::Error: Into<BoxError>,
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
        let json = if let Ok(Some(mime)) = req.mime_type() {
            mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)
        } else {
            false
        };
        if !json {
            return Err(crate::app_param_check_error_with_msg!(i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_FORMAT),));
        }

        let len = req.headers().get(&CONTENT_LENGTH).and_then(|l| l.to_str().ok()).and_then(|s| s.parse::<usize>().ok());

        let application = match req.get_application() {
            Ok(app) => app,
            Err(err) => {
                return Err(err);
            }
        };
        let limit = application.get_server_config()?.max_payload_size;
        if let Some(content_length) = len {
            if content_length > limit {
                return Err(crate::app_param_check_error_with_msg!(i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_TOO_LONG),));
            }
        }

        let (req, mut payload) = req.into_parts();
        let mut body = Vec::with_capacity(8192);

        while let Some(item) = payload.data().await {
            let data = item.map_err(|err| crate::app_system_error!("{:?}", err.into()))?;
            let chunk = data.chunk();
            if (body.len() + chunk.len()) > limit {
                return Err(crate::app_param_check_error_with_msg!(i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_TOO_LONG),));
            } else {
                body.extend_from_slice(chunk);
            }
        }
        let route_config = req.get_route_config()?;
        if let Ok(Some(log_param)) = req.get_request_log_param() {
            log_param.set_raw_param_lossy(String::from_utf8_lossy(body.as_slice()));
        }
        let session = Session::from_http_request(&req).await?;
        let data = serde_json::from_slice::<D>(&body).map_err(|err| {
            crate::app_param_check_error_with_msg!(
                i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_PARAM_ANALYZE_FAILED, ("reason", err.to_string().as_str())),
                "{:?}. source: {}",
                err,
                String::from_utf8_lossy(&body),
            )
        })?;
        if let Ok(Some(log_param)) = req.get_request_log_param() {
            log_param.set_param_val(&data);
        }
        data.validate_with_groups(&session, &route_config.validate_group).await?;
        Ok(HttpReqJson::new(data, application, session))
    }
}

#[async_trait]
impl<D, S, B> FromRequest<S, B> for HttpReqParam<D>
where
    D: DeserializeOwned + Serialize + Validated + Send + Sync + 'static,
    B: Body + Debug + Unpin + Send + Sync + 'static,
    B::Data: Send,
    B::Error: Into<BoxError>,
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
        let len = req.headers().get(&CONTENT_LENGTH).and_then(|l| l.to_str().ok()).and_then(|s| s.parse::<usize>().ok());

        let application = match req.get_application() {
            Ok(app) => app,
            Err(err) => {
                return Err(err);
            }
        };
        let limit = application.get_server_config()?.max_payload_size;
        if let Some(content_length) = len {
            if content_length > limit {
                return Err(crate::app_param_check_error_with_msg!(
                    i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_FORMAT),
                    "content is too long: {}",
                    content_length
                ));
            }
        }

        let (req, mut payload) = req.into_parts();
        let mut body = Vec::with_capacity(8192);

        let route_config = req.get_route_config()?;
        while let Some(item) = payload.data().await {
            let data = item.map_err(|err| crate::app_system_error!("{:?}", err.into()))?;
            let chunk = data.chunk();
            if (body.len() + chunk.len()) > limit {
                return Err(crate::app_param_check_error_with_msg!(
                    i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_FORMAT),
                    "content overflow",
                ));
            } else {
                body.extend_from_slice(chunk);
            }
        }

        let mut param_str = req.query_string().to_owned();
        let body_str = String::from_utf8(body).map_err(|err| crate::app_system_error!("{:?}", err))?;

        if !body_str.is_empty() {
            if !param_str.is_empty() {
                param_str.push('&');
            }
            param_str.push_str(body_str.as_str());
        }
        let parse = url_decode(param_str.as_str())?;
        if let Ok(Some(log_param)) = req.get_request_log_param() {
            log_param.set_raw_param_lossy(Cow::Borrowed(parse.as_str()));
        }

        let session = Session::from_http_request(&req).await?;

        let data: D = serde_qs::from_str::<D>(parse.as_str()).map(|val| Ok(val)).unwrap_or_else(|e| {
            tracing::error!(
                "Failed during Query extractor deserialization. \
                     Request path: {:?}, error: {:?}",
                req.path(),
                e
            );
            Err(crate::app_param_check_error_with_msg!(
                i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_PARAM_ANALYZE_FAILED, ("reason", e.to_string().as_str())),
                "{:?}",
                e
            ))
        })?;

        if let Ok(Some(log_param)) = req.get_request_log_param() {
            log_param.set_param_val(&data);
        }
        data.validate_with_groups(&session, &route_config.validate_group).await?;
        Ok(HttpReqParam::new(data, application, session))
    }
}

#[async_trait]
impl<D, S> FromRequestParts<S> for HttpReqPath<D>
where
    D: DeserializeOwned + Serialize + Validated + Send + Sync + 'static,
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request_parts(req: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
        let application = match req.get_application() {
            Ok(app) => app,
            Err(err) => {
                return Err(err);
            }
        };
        let route_config = req.get_route_config()?;
        if let Ok(Some(log_param)) = req.get_request_log_param() {
            log_param.set_raw_param(req.match_info().get_ref().to_string());
        }
        let session = Session::from_http_request(req).await?;

        let path: Path<D> = Path::from_request_parts(req, state).await.map_err(app_error_from!())?;

        let data: D = path.0;
        if let Ok(Some(log_param)) = req.get_request_log_param() {
            log_param.set_param_val(&data);
        }
        data.validate_with_groups(&session, &route_config.validate_group).await?;
        Ok(HttpReqPath::new(vec![], data, application, session))
    }
}

#[async_trait]
impl<D, S, B> FromRequest<S, B> for HttpReqMultipart<D>
where
    D: DeserializeOwned + Serialize + Validated + Send + Sync + 'static,
    B: Body<Data = Bytes> + Debug + Unpin + Send + Sync + 'static,
    B::Data: Send,
    B::Error: Into<BoxError>,
    S: Send + Sync,
{
    type Rejection = AppError;

    async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
        let content_type = match req.mime_type() {
            Ok(v) => v,
            Err(err) => {
                tracing::error!("{}", err);
                None
            }
        };
        if content_type.is_none() {
            return Err(crate::app_param_check_error_with_msg!(
                i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_FORMAT),
                "empty content-type",
            ));
        }
        if let Some(mime) = content_type {
            if mime.subtype() != mime::FORM_DATA {
                return Err(crate::app_param_check_error_with_msg!(
                    i18n_string!(SystemMessage::ERROR_HTTP_REQUEST_FORMAT),
                    "invalid content-type: {}",
                    mime.to_string().as_str()
                ));
            }
        }
        let application = match req.get_application() {
            Ok(app) => app,
            Err(err) => {
                return Err(err);
            }
        };
        let application3 = application.to_owned();
        let application4 = application.to_owned();
        let (req, payload) = req.into_parts();
        let route_config = req.get_route_config()?;
        let session = Session::from_http_request(&req).await?;
        let payload = Multipart::new(&req.headers, req.query_string(), payload, application3);
        match MultipartData::from_multipart(payload).await {
            Ok(data) => {
                if let Ok(Some(log_param)) = req.get_request_log_param() {
                    log_param.set_raw_param(data.to_json_string());
                }
                let deserializer = MultipartDeserializer::new(data).map_err(app_error_from!())?;
                match D::deserialize(deserializer) {
                    Ok(data) => {
                        if let Ok(Some(log_param)) = req.get_request_log_param() {
                            log_param.set_param_val(&data);
                        }
                        data.validate_with_groups(&session, &route_config.validate_group).await?;
                        Ok(HttpReqMultipart::new(data, application4, session))
                    }
                    Err(err) => Err(app_error_from!(err)),
                }
            }
            Err(err) => Err(err),
        }
    }
}