actix_web/error/
response_error.rs

1//! `ResponseError` trait and foreign impls.
2
3use std::{
4    convert::Infallible,
5    error::Error as StdError,
6    fmt,
7    io::{self, Write as _},
8};
9
10use bytes::BytesMut;
11
12use crate::{
13    body::BoxBody,
14    error::{downcast_dyn, downcast_get_type_id},
15    helpers,
16    http::{
17        header::{self, TryIntoHeaderValue},
18        StatusCode,
19    },
20    HttpResponse,
21};
22
23/// Errors that can generate responses.
24// TODO: flesh out documentation
25pub trait ResponseError: fmt::Debug + fmt::Display {
26    /// Returns appropriate status code for error.
27    ///
28    /// A 500 Internal Server Error is used by default. If [error_response](Self::error_response) is
29    /// also implemented and does not call `self.status_code()`, then this will not be used.
30    fn status_code(&self) -> StatusCode {
31        StatusCode::INTERNAL_SERVER_ERROR
32    }
33
34    /// Creates full response for error.
35    ///
36    /// By default, the generated response uses a 500 Internal Server Error status code, a
37    /// `Content-Type` of `text/plain`, and the body is set to `Self`'s `Display` impl.
38    fn error_response(&self) -> HttpResponse<BoxBody> {
39        let mut res = HttpResponse::new(self.status_code());
40
41        let mut buf = BytesMut::new();
42        let _ = write!(helpers::MutWriter(&mut buf), "{}", self);
43
44        let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();
45        res.headers_mut().insert(header::CONTENT_TYPE, mime);
46
47        res.set_body(BoxBody::new(buf))
48    }
49
50    downcast_get_type_id!();
51}
52
53downcast_dyn!(ResponseError);
54
55impl ResponseError for Box<dyn StdError + 'static> {}
56
57impl ResponseError for Infallible {
58    fn status_code(&self) -> StatusCode {
59        match *self {}
60    }
61    fn error_response(&self) -> HttpResponse<BoxBody> {
62        match *self {}
63    }
64}
65
66#[cfg(feature = "openssl")]
67impl ResponseError for actix_tls::accept::openssl::reexports::Error {}
68
69impl ResponseError for serde::de::value::Error {
70    fn status_code(&self) -> StatusCode {
71        StatusCode::BAD_REQUEST
72    }
73}
74
75impl ResponseError for serde_json::Error {}
76
77impl ResponseError for serde_urlencoded::ser::Error {}
78
79impl ResponseError for std::str::Utf8Error {
80    fn status_code(&self) -> StatusCode {
81        StatusCode::BAD_REQUEST
82    }
83}
84
85impl ResponseError for std::io::Error {
86    fn status_code(&self) -> StatusCode {
87        match self.kind() {
88            io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
89            io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
90            _ => StatusCode::INTERNAL_SERVER_ERROR,
91        }
92    }
93}
94
95impl ResponseError for actix_http::error::HttpError {}
96
97impl ResponseError for actix_http::Error {
98    fn status_code(&self) -> StatusCode {
99        StatusCode::INTERNAL_SERVER_ERROR
100    }
101
102    fn error_response(&self) -> HttpResponse<BoxBody> {
103        HttpResponse::with_body(self.status_code(), self.to_string()).map_into_boxed_body()
104    }
105}
106
107impl ResponseError for actix_http::header::InvalidHeaderValue {
108    fn status_code(&self) -> StatusCode {
109        StatusCode::BAD_REQUEST
110    }
111}
112
113impl ResponseError for actix_http::error::ParseError {
114    fn status_code(&self) -> StatusCode {
115        StatusCode::BAD_REQUEST
116    }
117}
118
119impl ResponseError for actix_http::error::PayloadError {
120    fn status_code(&self) -> StatusCode {
121        match *self {
122            actix_http::error::PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
123            _ => StatusCode::BAD_REQUEST,
124        }
125    }
126}
127
128impl ResponseError for actix_http::error::ContentTypeError {
129    fn status_code(&self) -> StatusCode {
130        StatusCode::BAD_REQUEST
131    }
132}
133
134#[cfg(feature = "ws")]
135impl ResponseError for actix_http::ws::HandshakeError {
136    fn error_response(&self) -> HttpResponse<BoxBody> {
137        actix_http::Response::from(self)
138            .map_into_boxed_body()
139            .into()
140    }
141}
142
143#[cfg(feature = "ws")]
144impl ResponseError for actix_http::ws::ProtocolError {}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_error_casting() {
152        use actix_http::error::{ContentTypeError, PayloadError};
153
154        let err = PayloadError::Overflow;
155        let resp_err: &dyn ResponseError = &err;
156
157        let err = resp_err.downcast_ref::<PayloadError>().unwrap();
158        assert_eq!(err.to_string(), "payload reached size limit");
159
160        let not_err = resp_err.downcast_ref::<ContentTypeError>();
161        assert!(not_err.is_none());
162    }
163}