actix_web/error/
response_error.rs1use std::{
4 convert::Infallible,
5 error::Error as StdError,
6 fmt,
7 io::{self, Write as _},
8};
9
10use actix_http::Response;
11use bytes::BytesMut;
12
13use crate::{
14 body::BoxBody,
15 error::{downcast_dyn, downcast_get_type_id},
16 helpers,
17 http::{
18 header::{self, TryIntoHeaderValue},
19 StatusCode,
20 },
21 HttpResponse,
22};
23
24pub trait ResponseError: fmt::Debug + fmt::Display {
27 fn status_code(&self) -> StatusCode {
32 StatusCode::INTERNAL_SERVER_ERROR
33 }
34
35 fn error_response(&self) -> HttpResponse<BoxBody> {
40 let mut res = HttpResponse::new(self.status_code());
41
42 let mut buf = BytesMut::new();
43 let _ = write!(helpers::MutWriter(&mut buf), "{}", self);
44
45 let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();
46 res.headers_mut().insert(header::CONTENT_TYPE, mime);
47
48 res.set_body(BoxBody::new(buf))
49 }
50
51 downcast_get_type_id!();
52}
53
54downcast_dyn!(ResponseError);
55
56impl ResponseError for Box<dyn StdError + 'static> {}
57
58impl ResponseError for Infallible {
59 fn status_code(&self) -> StatusCode {
60 match *self {}
61 }
62 fn error_response(&self) -> HttpResponse<BoxBody> {
63 match *self {}
64 }
65}
66
67#[cfg(feature = "openssl")]
68impl ResponseError for actix_tls::accept::openssl::reexports::Error {}
69
70impl ResponseError for serde::de::value::Error {
71 fn status_code(&self) -> StatusCode {
72 StatusCode::BAD_REQUEST
73 }
74}
75
76impl ResponseError for serde_json::Error {}
77
78impl ResponseError for serde_urlencoded::ser::Error {}
79
80impl ResponseError for std::str::Utf8Error {
81 fn status_code(&self) -> StatusCode {
82 StatusCode::BAD_REQUEST
83 }
84}
85
86impl ResponseError for std::io::Error {
87 fn status_code(&self) -> StatusCode {
88 match self.kind() {
89 io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
90 io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
91 _ => StatusCode::INTERNAL_SERVER_ERROR,
92 }
93 }
94}
95
96impl ResponseError for actix_http::error::HttpError {}
97
98impl ResponseError for actix_http::Error {
99 fn status_code(&self) -> StatusCode {
100 StatusCode::INTERNAL_SERVER_ERROR
101 }
102
103 fn error_response(&self) -> HttpResponse<BoxBody> {
104 HttpResponse::with_body(self.status_code(), self.to_string()).map_into_boxed_body()
105 }
106}
107
108impl ResponseError for actix_http::header::InvalidHeaderValue {
109 fn status_code(&self) -> StatusCode {
110 StatusCode::BAD_REQUEST
111 }
112}
113
114impl ResponseError for actix_http::error::ParseError {
115 fn status_code(&self) -> StatusCode {
116 StatusCode::BAD_REQUEST
117 }
118}
119
120impl ResponseError for actix_http::error::PayloadError {
121 fn status_code(&self) -> StatusCode {
122 match *self {
123 actix_http::error::PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
124 _ => StatusCode::BAD_REQUEST,
125 }
126 }
127}
128
129impl ResponseError for actix_http::ws::ProtocolError {}
130
131impl ResponseError for actix_http::error::ContentTypeError {
132 fn status_code(&self) -> StatusCode {
133 StatusCode::BAD_REQUEST
134 }
135}
136
137impl ResponseError for actix_http::ws::HandshakeError {
138 fn error_response(&self) -> HttpResponse<BoxBody> {
139 Response::from(self).map_into_boxed_body().into()
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_error_casting() {
149 use actix_http::error::{ContentTypeError, PayloadError};
150
151 let err = PayloadError::Overflow;
152 let resp_err: &dyn ResponseError = &err;
153
154 let err = resp_err.downcast_ref::<PayloadError>().unwrap();
155 assert_eq!(err.to_string(), "payload reached size limit");
156
157 let not_err = resp_err.downcast_ref::<ContentTypeError>();
158 assert!(not_err.is_none());
159 }
160}