1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-08-23 14:05:14 +02:00

fix(multipart): optional content-disposition for non-form-data requests (#3416)

This commit is contained in:
Rob Ede
2024-07-01 03:55:08 +01:00
committed by GitHub
parent 668b8e5745
commit 71cd3a31f9
16 changed files with 479 additions and 248 deletions

View File

@@ -11,77 +11,95 @@ use derive_more::{Display, Error, From};
#[derive(Debug, Display, From, Error)]
#[non_exhaustive]
pub enum MultipartError {
/// Content-Disposition header is not found or is not equal to "form-data".
/// Could not find Content-Type header.
#[display(fmt = "Could not find Content-Type header")]
ContentTypeMissing,
/// Could not parse Content-Type header.
#[display(fmt = "Could not parse Content-Type header")]
ContentTypeParse,
/// Parsed Content-Type did not have "multipart" top-level media type.
///
/// According to [RFC 7578 §4.2](https://datatracker.ietf.org/doc/html/rfc7578#section-4.2) a
/// Content-Disposition header must always be present and equal to "form-data".
#[display(fmt = "No Content-Disposition `form-data` header")]
NoContentDisposition,
/// Also raised when extracting a [`MultipartForm`] from a request that does not have the
/// "multipart/form-data" media type.
///
/// [`MultipartForm`]: struct@crate::form::MultipartForm
#[display(fmt = "Parsed Content-Type did not have "multipart" top-level media type")]
ContentTypeIncompatible,
/// Content-Type header is not found
#[display(fmt = "No Content-Type header found")]
NoContentType,
/// Can not parse Content-Type header
#[display(fmt = "Can not parse Content-Type header")]
ParseContentType,
/// Multipart boundary is not found
/// Multipart boundary is not found.
#[display(fmt = "Multipart boundary is not found")]
Boundary,
BoundaryMissing,
/// Nested multipart is not supported
/// Content-Disposition header was not found or not of disposition type "form-data" when parsing
/// a "form-data" field.
///
/// As per [RFC 7578 §4.2], a "multipart/form-data" field's Content-Disposition header must
/// always be present and have a disposition type of "form-data".
///
/// [RFC 7578 §4.2]: https://datatracker.ietf.org/doc/html/rfc7578#section-4.2
#[display(fmt = "Content-Disposition header was not found when parsing a \"form-data\" field")]
ContentDispositionMissing,
/// Content-Disposition name parameter was not found when parsing a "form-data" field.
///
/// As per [RFC 7578 §4.2], a "multipart/form-data" field's Content-Disposition header must
/// always include a "name" parameter.
///
/// [RFC 7578 §4.2]: https://datatracker.ietf.org/doc/html/rfc7578#section-4.2
#[display(fmt = "Content-Disposition header was not found when parsing a \"form-data\" field")]
ContentDispositionNameMissing,
/// Nested multipart is not supported.
#[display(fmt = "Nested multipart is not supported")]
Nested,
/// Multipart stream is incomplete
/// Multipart stream is incomplete.
#[display(fmt = "Multipart stream is incomplete")]
Incomplete,
/// Error during field parsing
#[display(fmt = "{}", _0)]
/// Field parsing failed.
#[display(fmt = "Error during field parsing")]
Parse(ParseError),
/// Payload error
#[display(fmt = "{}", _0)]
/// HTTP payload error.
#[display(fmt = "Payload error")]
Payload(PayloadError),
/// Not consumed
#[display(fmt = "Multipart stream is not consumed")]
/// Stream is not consumed.
#[display(fmt = "Stream is not consumed")]
NotConsumed,
/// An error from a field handler in a form
#[display(
fmt = "An error occurred processing field `{}`: {}",
field_name,
source
)]
/// Form field handler raised error.
#[display(fmt = "An error occurred processing field: {name}")]
Field {
field_name: String,
name: String,
source: actix_web::Error,
},
/// Duplicate field
#[display(fmt = "Duplicate field found for: `{}`", _0)]
/// Duplicate field found (for structure that opted-in to denying duplicate fields).
#[display(fmt = "Duplicate field found: {_0}")]
#[from(ignore)]
DuplicateField(#[error(not(source))] String),
/// Missing field
#[display(fmt = "Field with name `{}` is required", _0)]
/// Required field is missing.
#[display(fmt = "Required field is missing: {_0}")]
#[from(ignore)]
MissingField(#[error(not(source))] String),
/// Unknown field
#[display(fmt = "Unsupported field `{}`", _0)]
/// Unknown field (for structure that opted-in to denying unknown fields).
#[display(fmt = "Unknown field: {_0}")]
#[from(ignore)]
UnsupportedField(#[error(not(source))] String),
UnknownField(#[error(not(source))] String),
}
/// Return `BadRequest` for `MultipartError`
/// Return `BadRequest` for `MultipartError`.
impl ResponseError for MultipartError {
fn status_code(&self) -> StatusCode {
match &self {
MultipartError::Field { source, .. } => source.as_response_error().status_code(),
MultipartError::ContentTypeIncompatible => StatusCode::UNSUPPORTED_MEDIA_TYPE,
_ => StatusCode::BAD_REQUEST,
}
}
@@ -93,7 +111,7 @@ mod tests {
#[test]
fn test_multipart_error() {
let resp = MultipartError::Boundary.error_response();
let resp = MultipartError::BoundaryMissing.error_response();
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
}