actix-form-data/src/error.rs

182 lines
6 KiB
Rust

/*
* This file is part of Actix Form Data.
*
* Copyright © 2020 Riley Trautman
*
* Actix Form Data is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Actix Form Data is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Actix Form Data. If not, see <http://www.gnu.org/licenses/>.
*/
use std::{
num::{ParseFloatError, ParseIntError},
string::FromUtf8Error,
};
use actix_web::{
error::{ParseError, PayloadError, ResponseError},
http::StatusCode,
HttpResponse,
};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Error parsing payload")]
Payload(#[from] PayloadError),
#[error("Error in multipart creation")]
Multipart(#[from] MultipartError),
#[error("Failed to parse field")]
ParseField(#[from] FromUtf8Error),
#[error("Failed to parse int")]
ParseInt(#[from] ParseIntError),
#[error("Failed to parse float")]
ParseFloat(#[from] ParseFloatError),
#[error("Bad Content-Type")]
ContentType,
#[error("Bad Content-Disposition")]
ContentDisposition,
#[error("Failed to parse field name")]
Field,
#[error("Too many fields in request")]
FieldCount,
#[error("Field too large")]
FieldSize,
#[error("Found field with unexpected name or type")]
FieldType,
#[error("Failed to parse filename")]
Filename,
#[error("Too many files in request")]
FileCount,
#[error("File too large")]
FileSize,
#[error("Task panicked")]
Panicked,
}
#[derive(Debug, thiserror::Error)]
pub enum MultipartError {
#[error("No Content-Disposition `form-data` header")]
NoContentDisposition,
#[error("No Content-Type header found")]
NoContentType,
#[error("Cannot parse Content-Type header")]
ParseContentType,
#[error("Multipart boundary is not found")]
Boundary,
#[error("Nested multipart is not supported")]
Nested,
#[error("Multipart stream is incomplete")]
Incomplete,
#[error("Failed parsing")]
Parse(#[source] ParseError),
#[error("Multipart stream is not consumed")]
NotConsumed,
#[error("An error occured processing field `{field_name}`: `{zource}`")]
Field { field_name: String, zource: String },
#[error("Duplicate field found for: `{0}")]
DuplicateField(String),
#[error("Field with name `{0}` is required")]
MissingField(String),
#[error("Unsupported field `{0}`")]
UnsupportedField(String),
#[error("Unknown error occured: {0}")]
Unknown(String),
}
impl From<actix_multipart::MultipartError> for Error {
fn from(value: actix_multipart::MultipartError) -> Self {
match value {
actix_multipart::MultipartError::NoContentDisposition => {
Error::Multipart(MultipartError::NoContentDisposition)
}
actix_multipart::MultipartError::NoContentType => {
Error::Multipart(MultipartError::NoContentType)
}
actix_multipart::MultipartError::ParseContentType => {
Error::Multipart(MultipartError::ParseContentType)
}
actix_multipart::MultipartError::Boundary => Error::Multipart(MultipartError::Boundary),
actix_multipart::MultipartError::Nested => Error::Multipart(MultipartError::Nested),
actix_multipart::MultipartError::Incomplete => {
Error::Multipart(MultipartError::Incomplete)
}
actix_multipart::MultipartError::Parse(e) => Error::Multipart(MultipartError::Parse(e)),
actix_multipart::MultipartError::Payload(e) => Error::Payload(e),
actix_multipart::MultipartError::NotConsumed => {
Error::Multipart(MultipartError::NotConsumed)
}
actix_multipart::MultipartError::Field { field_name, source } => {
Error::Multipart(MultipartError::Field {
field_name,
zource: source.to_string(),
})
}
actix_multipart::MultipartError::DuplicateField(s) => {
Error::Multipart(MultipartError::DuplicateField(s))
}
actix_multipart::MultipartError::MissingField(s) => {
Error::Multipart(MultipartError::MissingField(s))
}
actix_multipart::MultipartError::UnsupportedField(s) => {
Error::Multipart(MultipartError::UnsupportedField(s))
}
e => Error::Multipart(MultipartError::Unknown(e.to_string())),
}
}
}
impl From<tokio::task::JoinError> for Error {
fn from(_: tokio::task::JoinError) -> Self {
Self::Panicked
}
}
impl ResponseError for Error {
fn status_code(&self) -> StatusCode {
match *self {
Error::Payload(ref e) => e.status_code(),
_ => StatusCode::BAD_REQUEST,
}
}
fn error_response(&self) -> HttpResponse {
match *self {
Error::Payload(ref e) => e.error_response(),
Error::Panicked => HttpResponse::InternalServerError().finish(),
Error::Multipart(_)
| Error::ParseField(_)
| Error::ParseInt(_)
| Error::ParseFloat(_) => HttpResponse::BadRequest().finish(),
Error::ContentType
| Error::ContentDisposition
| Error::Field
| Error::FieldCount
| Error::FieldSize
| Error::FieldType
| Error::Filename
| Error::FileCount
| Error::FileSize => HttpResponse::BadRequest().finish(),
}
}
}
#[cfg(test)]
mod tests {
use super::Error;
#[test]
fn assert_send() {
fn is_send<E: Send>() {}
is_send::<Error>();
}
}