2021-08-07 18:03:21 +02:00
|
|
|
use crate::response::IntoResponse;
|
|
|
|
|
|
2021-08-03 21:55:48 +02:00
|
|
|
use super::{rejection::*, FromRequest, RequestParts};
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
|
|
|
|
|
/// Extractor that will reject requests with a body larger than some size.
|
|
|
|
|
///
|
|
|
|
|
/// # Example
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,no_run
|
2021-08-18 00:04:15 +02:00
|
|
|
/// use axum::{
|
|
|
|
|
/// extract::ContentLengthLimit,
|
|
|
|
|
/// handler::post,
|
2021-08-19 22:37:48 +02:00
|
|
|
/// Router,
|
2021-08-18 00:04:15 +02:00
|
|
|
/// };
|
2021-08-03 21:55:48 +02:00
|
|
|
///
|
2021-08-18 00:04:15 +02:00
|
|
|
/// async fn handler(body: ContentLengthLimit<String, 1024>) {
|
2021-08-03 21:55:48 +02:00
|
|
|
/// // ...
|
|
|
|
|
/// }
|
|
|
|
|
///
|
2021-08-19 22:37:48 +02:00
|
|
|
/// let app = Router::new().route("/", post(handler));
|
2021-08-03 21:55:48 +02:00
|
|
|
/// # async {
|
2021-08-04 15:38:51 +02:00
|
|
|
/// # axum::Server::bind(&"".parse().unwrap()).serve(app.into_make_service()).await.unwrap();
|
2021-08-03 21:55:48 +02:00
|
|
|
/// # };
|
|
|
|
|
/// ```
|
|
|
|
|
///
|
|
|
|
|
/// This requires the request to have a `Content-Length` header.
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct ContentLengthLimit<T, const N: u64>(pub T);
|
|
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
|
impl<T, B, const N: u64> FromRequest<B> for ContentLengthLimit<T, N>
|
|
|
|
|
where
|
|
|
|
|
T: FromRequest<B>,
|
2021-08-07 18:03:21 +02:00
|
|
|
T::Rejection: IntoResponse,
|
2021-08-03 21:55:48 +02:00
|
|
|
B: Send,
|
|
|
|
|
{
|
|
|
|
|
type Rejection = ContentLengthLimitRejection<T::Rejection>;
|
|
|
|
|
|
|
|
|
|
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
|
|
|
|
|
let content_length = req
|
|
|
|
|
.headers()
|
|
|
|
|
.ok_or(ContentLengthLimitRejection::HeadersAlreadyExtracted(
|
|
|
|
|
HeadersAlreadyExtracted,
|
|
|
|
|
))?
|
|
|
|
|
.get(http::header::CONTENT_LENGTH);
|
|
|
|
|
|
|
|
|
|
let content_length =
|
|
|
|
|
content_length.and_then(|value| value.to_str().ok()?.parse::<u64>().ok());
|
|
|
|
|
|
|
|
|
|
if let Some(length) = content_length {
|
|
|
|
|
if length > N {
|
|
|
|
|
return Err(ContentLengthLimitRejection::PayloadTooLarge(
|
|
|
|
|
PayloadTooLarge,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return Err(ContentLengthLimitRejection::LengthRequired(LengthRequired));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let value = T::from_request(req)
|
|
|
|
|
.await
|
|
|
|
|
.map_err(ContentLengthLimitRejection::Inner)?;
|
|
|
|
|
|
|
|
|
|
Ok(Self(value))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T, const N: u64> Deref for ContentLengthLimit<T, N> {
|
|
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
|
&self.0
|
|
|
|
|
}
|
|
|
|
|
}
|