actix_web/error/
mod.rs

1//! Error and Result module
2
3// This is meant to be a glob import of the whole error module except for `Error`. Rustdoc can't yet
4// correctly resolve the conflicting `Error` type defined in this module, so these re-exports are
5// expanded manually.
6//
7// See <https://github.com/rust-lang/rust/issues/83375>
8pub use actix_http::error::{ContentTypeError, DispatchError, HttpError, ParseError, PayloadError};
9use derive_more::{Display, Error, From};
10use serde_json::error::Error as JsonError;
11use serde_urlencoded::{de::Error as FormDeError, ser::Error as FormError};
12use url::ParseError as UrlParseError;
13
14use crate::http::StatusCode;
15
16#[allow(clippy::module_inception)]
17mod error;
18mod internal;
19mod macros;
20mod response_error;
21
22pub(crate) use self::macros::{downcast_dyn, downcast_get_type_id};
23pub use self::{error::Error, internal::*, response_error::ResponseError};
24
25/// A convenience [`Result`](std::result::Result) for Actix Web operations.
26///
27/// This type alias is generally used to avoid writing out `actix_http::Error` directly.
28pub type Result<T, E = Error> = std::result::Result<T, E>;
29
30/// An error representing a problem running a blocking task on a thread pool.
31#[derive(Debug, Display, Error)]
32#[display("Blocking thread pool is shut down unexpectedly")]
33#[non_exhaustive]
34pub struct BlockingError;
35
36impl ResponseError for crate::error::BlockingError {}
37
38/// Errors which can occur when attempting to generate resource uri.
39#[derive(Debug, PartialEq, Eq, Display, Error, From)]
40#[non_exhaustive]
41pub enum UrlGenerationError {
42    /// Resource not found.
43    #[display("Resource not found")]
44    ResourceNotFound,
45
46    /// Not all URL parameters covered.
47    #[display("Not all URL parameters covered")]
48    NotEnoughElements,
49
50    /// URL parse error.
51    #[display("{}", _0)]
52    ParseError(UrlParseError),
53}
54
55impl ResponseError for UrlGenerationError {}
56
57/// A set of errors that can occur during parsing urlencoded payloads
58#[derive(Debug, Display, Error, From)]
59#[non_exhaustive]
60pub enum UrlencodedError {
61    /// Can not decode chunked transfer encoding.
62    #[display("Can not decode chunked transfer encoding.")]
63    Chunked,
64
65    /// Payload size is larger than allowed. (default limit: 256kB).
66    #[display(
67        "URL encoded payload is larger ({} bytes) than allowed (limit: {} bytes).",
68        size,
69        limit
70    )]
71    Overflow { size: usize, limit: usize },
72
73    /// Payload size is now known.
74    #[display("Payload size is now known.")]
75    UnknownLength,
76
77    /// Content type error.
78    #[display("Content type error.")]
79    ContentType,
80
81    /// Parse error.
82    #[display("Parse error: {}.", _0)]
83    Parse(FormDeError),
84
85    /// Encoding error.
86    #[display("Encoding error.")]
87    Encoding,
88
89    /// Serialize error.
90    #[display("Serialize error: {}.", _0)]
91    Serialize(FormError),
92
93    /// Payload error.
94    #[display("Error that occur during reading payload: {}.", _0)]
95    Payload(PayloadError),
96}
97
98impl ResponseError for UrlencodedError {
99    fn status_code(&self) -> StatusCode {
100        match self {
101            Self::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
102            Self::UnknownLength => StatusCode::LENGTH_REQUIRED,
103            Self::ContentType => StatusCode::UNSUPPORTED_MEDIA_TYPE,
104            Self::Payload(err) => err.status_code(),
105            _ => StatusCode::BAD_REQUEST,
106        }
107    }
108}
109
110/// A set of errors that can occur during parsing json payloads
111#[derive(Debug, Display, Error)]
112#[non_exhaustive]
113pub enum JsonPayloadError {
114    /// Payload size is bigger than allowed & content length header set. (default: 2MB)
115    #[display(
116        "JSON payload ({} bytes) is larger than allowed (limit: {} bytes).",
117        length,
118        limit
119    )]
120    OverflowKnownLength { length: usize, limit: usize },
121
122    /// Payload size is bigger than allowed but no content length header set. (default: 2MB)
123    #[display("JSON payload has exceeded limit ({} bytes).", limit)]
124    Overflow { limit: usize },
125
126    /// Content type error
127    #[display("Content type error")]
128    ContentType,
129
130    /// Deserialize error
131    #[display("Json deserialize error: {}", _0)]
132    Deserialize(JsonError),
133
134    /// Serialize error
135    #[display("Json serialize error: {}", _0)]
136    Serialize(JsonError),
137
138    /// Payload error
139    #[display("Error that occur during reading payload: {}", _0)]
140    Payload(PayloadError),
141}
142
143impl From<PayloadError> for JsonPayloadError {
144    fn from(err: PayloadError) -> Self {
145        Self::Payload(err)
146    }
147}
148
149impl ResponseError for JsonPayloadError {
150    fn status_code(&self) -> StatusCode {
151        match self {
152            Self::OverflowKnownLength {
153                length: _,
154                limit: _,
155            } => StatusCode::PAYLOAD_TOO_LARGE,
156            Self::Overflow { limit: _ } => StatusCode::PAYLOAD_TOO_LARGE,
157            Self::Serialize(_) => StatusCode::INTERNAL_SERVER_ERROR,
158            Self::Payload(err) => err.status_code(),
159            _ => StatusCode::BAD_REQUEST,
160        }
161    }
162}
163
164/// A set of errors that can occur during parsing request paths
165#[derive(Debug, Display, Error)]
166#[non_exhaustive]
167pub enum PathError {
168    /// Deserialize error
169    #[display("Path deserialize error: {}", _0)]
170    Deserialize(serde::de::value::Error),
171}
172
173/// Return `BadRequest` for `PathError`
174impl ResponseError for PathError {
175    fn status_code(&self) -> StatusCode {
176        StatusCode::BAD_REQUEST
177    }
178}
179
180/// A set of errors that can occur during parsing query strings.
181#[derive(Debug, Display, Error, From)]
182#[non_exhaustive]
183pub enum QueryPayloadError {
184    /// Query deserialize error.
185    #[display("Query deserialize error: {}", _0)]
186    Deserialize(serde::de::value::Error),
187}
188
189impl ResponseError for QueryPayloadError {
190    fn status_code(&self) -> StatusCode {
191        StatusCode::BAD_REQUEST
192    }
193}
194
195/// Error type returned when reading body as lines.
196#[derive(Debug, Display, Error, From)]
197#[non_exhaustive]
198pub enum ReadlinesError {
199    #[display("Encoding error")]
200    /// Payload size is bigger than allowed. (default: 256kB)
201    EncodingError,
202
203    /// Payload error.
204    #[display("Error that occur during reading payload: {}", _0)]
205    Payload(PayloadError),
206
207    /// Line limit exceeded.
208    #[display("Line limit exceeded")]
209    LimitOverflow,
210
211    /// ContentType error.
212    #[display("Content-type error")]
213    ContentTypeError(ContentTypeError),
214}
215
216impl ResponseError for ReadlinesError {
217    fn status_code(&self) -> StatusCode {
218        match *self {
219            ReadlinesError::LimitOverflow => StatusCode::PAYLOAD_TOO_LARGE,
220            _ => StatusCode::BAD_REQUEST,
221        }
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    #[test]
230    fn test_urlencoded_error() {
231        let resp = UrlencodedError::Overflow { size: 0, limit: 0 }.error_response();
232        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
233        let resp = UrlencodedError::UnknownLength.error_response();
234        assert_eq!(resp.status(), StatusCode::LENGTH_REQUIRED);
235        let resp = UrlencodedError::ContentType.error_response();
236        assert_eq!(resp.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
237    }
238
239    #[test]
240    fn test_json_payload_error() {
241        let resp = JsonPayloadError::OverflowKnownLength {
242            length: 0,
243            limit: 0,
244        }
245        .error_response();
246        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
247        let resp = JsonPayloadError::Overflow { limit: 0 }.error_response();
248        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
249        let resp = JsonPayloadError::ContentType.error_response();
250        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
251    }
252
253    #[test]
254    fn test_query_payload_error() {
255        let resp = QueryPayloadError::Deserialize(
256            serde_urlencoded::from_str::<i32>("bad query").unwrap_err(),
257        )
258        .error_response();
259        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
260    }
261
262    #[test]
263    fn test_readlines_error() {
264        let resp = ReadlinesError::LimitOverflow.error_response();
265        assert_eq!(resp.status(), StatusCode::PAYLOAD_TOO_LARGE);
266        let resp = ReadlinesError::EncodingError.error_response();
267        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
268    }
269}