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 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
23pub trait ResponseError: fmt::Debug + fmt::Display {
26 fn status_code(&self) -> StatusCode {
31 StatusCode::INTERNAL_SERVER_ERROR
32 }
33
34 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}