use crate::{Error, IntoResponse, Response, Result, StatusCode, ThisError};
#[derive(ThisError, Debug)]
pub enum PayloadError {
#[error("payload is empty")]
Empty,
#[error("failed to read payload")]
Read,
#[error("failed to parse payload")]
Parse,
#[error("multipart missing boundary")]
MissingBoundary,
#[error("parse utf8 failed, {0}")]
Utf8(#[from] std::string::FromUtf8Error),
#[error("{0}")]
Hyper(#[from] hyper::Error),
#[cfg(feature = "json")]
#[error("JSON serialize or deserialize faild, {0}")]
Json(#[from] serde_json::Error),
#[cfg(any(feature = "form", feature = "query"))]
#[error("url decode failed, {0}")]
UrlDecode(#[from] serde_urlencoded::de::Error),
#[error("content-length is required")]
LengthRequired,
#[error("payload is too large")]
TooLarge,
#[error("unsupported media type, `{}` is required", .0.to_string())]
UnsupportedMediaType(mime::Mime),
#[error("payload has been used")]
Used,
}
impl IntoResponse for PayloadError {
fn into_response(self) -> Response {
(
match self {
Self::Empty
| Self::Read
| Self::Parse
| Self::MissingBoundary
| Self::Utf8(_)
| Self::Hyper(_) => StatusCode::BAD_REQUEST,
#[cfg(feature = "json")]
Self::Json(_) => StatusCode::BAD_REQUEST,
#[cfg(any(feature = "form", feature = "query"))]
Self::UrlDecode(_) => StatusCode::BAD_REQUEST,
Self::LengthRequired => StatusCode::LENGTH_REQUIRED,
Self::TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
Self::UnsupportedMediaType(_) => StatusCode::UNSUPPORTED_MEDIA_TYPE,
Self::Used => StatusCode::INTERNAL_SERVER_ERROR,
},
self.to_string(),
)
.into_response()
}
}
impl From<PayloadError> for Error {
fn from(e: PayloadError) -> Self {
e.into_error()
}
}
pub trait Payload {
const NAME: &'static str = "payload";
const LIMIT: u64 = 1024 * 1024;
fn mime() -> mime::Mime;
fn detect(m: &mime::Mime) -> bool;
#[must_use]
#[inline]
fn limit(limit: Option<u64>) -> u64 {
limit.unwrap_or(Self::LIMIT)
}
#[inline]
fn check_type(m: Option<mime::Mime>) -> Result<mime::Mime, PayloadError> {
m.filter(Self::detect)
.ok_or_else(|| PayloadError::UnsupportedMediaType(Self::mime()))
}
#[inline]
fn check_length(len: Option<u64>, limit: Option<u64>) -> Result<(), PayloadError> {
len.map_or_else(
|| Err(PayloadError::LengthRequired),
|len| {
(len <= Self::limit(limit))
.then_some(())
.ok_or_else(|| PayloadError::TooLarge)
},
)
}
#[inline]
fn check_header(
m: Option<mime::Mime>,
len: Option<u64>,
limit: Option<u64>,
) -> Result<mime::Mime, PayloadError> {
let m = Self::check_type(m)?;
Self::check_length(len, limit)?;
Ok(m)
}
}