use std::error::Error;
use axum::{
RequestExt,
extract::{FromRequest, Request},
};
use futures_util::StreamExt;
use serde::de::DeserializeOwned;
use super::{
HttpError, MAX_REQUEST_BODY,
body::{body_of, no_body, require_json, require_same_origin, too_large},
};
#[cfg(test)]
mod tests;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Strict<T>(pub T);
impl<S, T> FromRequest<S> for Strict<T>
where
S: Send + Sync,
T: DeserializeOwned,
{
type Rejection = HttpError;
async fn from_request(request: Request, _: &S) -> Result<Self, HttpError> {
require_json(request.headers())?;
let (parts, body) = request.with_limited_body().into_parts();
let body = read(body).await?;
body_of(&parts, &body).map(Strict)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct NoBody;
impl<S: Send + Sync> FromRequest<S> for NoBody {
type Rejection = HttpError;
async fn from_request(request: Request, _: &S) -> Result<Self, HttpError> {
let (parts, body) = request.with_limited_body().into_parts();
require_same_origin(&parts)?;
let body = read(body).await?;
no_body(&parts, &body).map(|()| NoBody)
}
}
async fn read(body: axum::body::Body) -> Result<Vec<u8>, HttpError> {
let mut chunks = body.into_data_stream();
let mut read = Vec::new();
while let Some(chunk) = chunks.next().await {
let chunk = chunk.map_err(|failure| {
if exceeds_a_limit(&failure) {
HttpError::too_large("the body exceeds the limit of the server")
} else {
HttpError::malformed("the body could not be read")
}
})?;
if read.len() + chunk.len() > MAX_REQUEST_BODY {
return Err(too_large());
}
read.extend_from_slice(&chunk);
}
Ok(read)
}
fn exceeds_a_limit(failure: &axum::Error) -> bool {
let mut cause: Option<&(dyn Error + 'static)> = Some(failure);
while let Some(error) = cause {
if error.is::<http_body_util::LengthLimitError>() {
return true;
}
cause = error.source();
}
false
}