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),
}
}
}