actix_multipart/
error.rs

1//! Error and Result module
2
3use actix_web::{
4    error::{ParseError, PayloadError},
5    http::StatusCode,
6    ResponseError,
7};
8use derive_more::{Display, Error, From};
9
10/// A set of errors that can occur during parsing multipart streams.
11#[derive(Debug, Display, From, Error)]
12#[non_exhaustive]
13pub enum Error {
14    /// Could not find Content-Type header.
15    #[display(fmt = "Could not find Content-Type header")]
16    ContentTypeMissing,
17
18    /// Could not parse Content-Type header.
19    #[display(fmt = "Could not parse Content-Type header")]
20    ContentTypeParse,
21
22    /// Parsed Content-Type did not have "multipart" top-level media type.
23    ///
24    /// Also raised when extracting a [`MultipartForm`] from a request that does not have the
25    /// "multipart/form-data" media type.
26    ///
27    /// [`MultipartForm`]: struct@crate::form::MultipartForm
28    #[display(fmt = "Parsed Content-Type did not have "multipart" top-level media type")]
29    ContentTypeIncompatible,
30
31    /// Multipart boundary is not found.
32    #[display(fmt = "Multipart boundary is not found")]
33    BoundaryMissing,
34
35    /// Content-Disposition header was not found or not of disposition type "form-data" when parsing
36    /// a "form-data" field.
37    ///
38    /// As per [RFC 7578 §4.2], a "multipart/form-data" field's Content-Disposition header must
39    /// always be present and have a disposition type of "form-data".
40    ///
41    /// [RFC 7578 §4.2]: https://datatracker.ietf.org/doc/html/rfc7578#section-4.2
42    #[display(fmt = "Content-Disposition header was not found when parsing a \"form-data\" field")]
43    ContentDispositionMissing,
44
45    /// Content-Disposition name parameter was not found when parsing a "form-data" field.
46    ///
47    /// As per [RFC 7578 §4.2], a "multipart/form-data" field's Content-Disposition header must
48    /// always include a "name" parameter.
49    ///
50    /// [RFC 7578 §4.2]: https://datatracker.ietf.org/doc/html/rfc7578#section-4.2
51    #[display(fmt = "Content-Disposition header was not found when parsing a \"form-data\" field")]
52    ContentDispositionNameMissing,
53
54    /// Nested multipart is not supported.
55    #[display(fmt = "Nested multipart is not supported")]
56    Nested,
57
58    /// Multipart stream is incomplete.
59    #[display(fmt = "Multipart stream is incomplete")]
60    Incomplete,
61
62    /// Field parsing failed.
63    #[display(fmt = "Error during field parsing")]
64    Parse(ParseError),
65
66    /// HTTP payload error.
67    #[display(fmt = "Payload error")]
68    Payload(PayloadError),
69
70    /// Stream is not consumed.
71    #[display(fmt = "Stream is not consumed")]
72    NotConsumed,
73
74    /// Form field handler raised error.
75    #[display(fmt = "An error occurred processing field: {name}")]
76    Field {
77        name: String,
78        source: actix_web::Error,
79    },
80
81    /// Duplicate field found (for structure that opted-in to denying duplicate fields).
82    #[display(fmt = "Duplicate field found: {_0}")]
83    #[from(ignore)]
84    DuplicateField(#[error(not(source))] String),
85
86    /// Required field is missing.
87    #[display(fmt = "Required field is missing: {_0}")]
88    #[from(ignore)]
89    MissingField(#[error(not(source))] String),
90
91    /// Unknown field (for structure that opted-in to denying unknown fields).
92    #[display(fmt = "Unknown field: {_0}")]
93    #[from(ignore)]
94    UnknownField(#[error(not(source))] String),
95}
96
97/// Return `BadRequest` for `MultipartError`.
98impl ResponseError for Error {
99    fn status_code(&self) -> StatusCode {
100        match &self {
101            Error::Field { source, .. } => source.as_response_error().status_code(),
102            Error::ContentTypeIncompatible => StatusCode::UNSUPPORTED_MEDIA_TYPE,
103            _ => StatusCode::BAD_REQUEST,
104        }
105    }
106}
107
108#[cfg(test)]
109mod tests {
110    use super::*;
111
112    #[test]
113    fn test_multipart_error() {
114        let resp = Error::BoundaryMissing.error_response();
115        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
116    }
117}