1use std::{cell::RefCell, fmt, io::Write as _};
2
3use actix_http::{
4 body::BoxBody,
5 header::{self, TryIntoHeaderValue as _},
6 StatusCode,
7};
8use bytes::{BufMut as _, BytesMut};
9
10use crate::{Error, HttpRequest, HttpResponse, Responder, ResponseError};
11
12pub struct InternalError<T> {
28 cause: T,
29 status: InternalErrorType,
30}
31
32enum InternalErrorType {
33 Status(StatusCode),
34 Response(RefCell<Option<HttpResponse>>),
35}
36
37impl<T> InternalError<T> {
38 pub fn new(cause: T, status: StatusCode) -> Self {
40 InternalError {
41 cause,
42 status: InternalErrorType::Status(status),
43 }
44 }
45
46 pub fn from_response(cause: T, response: HttpResponse) -> Self {
48 InternalError {
49 cause,
50 status: InternalErrorType::Response(RefCell::new(Some(response))),
51 }
52 }
53}
54
55impl<T: fmt::Debug> fmt::Debug for InternalError<T> {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 self.cause.fmt(f)
58 }
59}
60
61impl<T: fmt::Display> fmt::Display for InternalError<T> {
62 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63 self.cause.fmt(f)
64 }
65}
66
67impl<T> ResponseError for InternalError<T>
68where
69 T: fmt::Debug + fmt::Display,
70{
71 fn status_code(&self) -> StatusCode {
72 match self.status {
73 InternalErrorType::Status(st) => st,
74 InternalErrorType::Response(ref resp) => {
75 if let Some(resp) = resp.borrow().as_ref() {
76 resp.head().status
77 } else {
78 StatusCode::INTERNAL_SERVER_ERROR
79 }
80 }
81 }
82 }
83
84 fn error_response(&self) -> HttpResponse {
85 match self.status {
86 InternalErrorType::Status(status) => {
87 let mut res = HttpResponse::new(status);
88 let mut buf = BytesMut::new().writer();
89 let _ = write!(buf, "{}", self);
90
91 let mime = mime::TEXT_PLAIN_UTF_8.try_into_value().unwrap();
92 res.headers_mut().insert(header::CONTENT_TYPE, mime);
93
94 res.set_body(BoxBody::new(buf.into_inner()))
95 }
96
97 InternalErrorType::Response(ref resp) => {
98 if let Some(resp) = resp.borrow_mut().take() {
99 resp
100 } else {
101 HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
102 }
103 }
104 }
105 }
106}
107
108impl<T> Responder for InternalError<T>
109where
110 T: fmt::Debug + fmt::Display + 'static,
111{
112 type Body = BoxBody;
113
114 fn respond_to(self, _: &HttpRequest) -> HttpResponse<Self::Body> {
115 HttpResponse::from_error(self)
116 }
117}
118
119macro_rules! error_helper {
120 ($name:ident, $status:ident) => {
121 #[doc = concat!("Helper function that wraps any error and generates a `", stringify!($status), "` response.")]
122 #[allow(non_snake_case)]
123 pub fn $name<T>(err: T) -> Error
124 where
125 T: fmt::Debug + fmt::Display + 'static,
126 {
127 InternalError::new(err, StatusCode::$status).into()
128 }
129 };
130}
131
132error_helper!(ErrorBadRequest, BAD_REQUEST);
133error_helper!(ErrorUnauthorized, UNAUTHORIZED);
134error_helper!(ErrorPaymentRequired, PAYMENT_REQUIRED);
135error_helper!(ErrorForbidden, FORBIDDEN);
136error_helper!(ErrorNotFound, NOT_FOUND);
137error_helper!(ErrorMethodNotAllowed, METHOD_NOT_ALLOWED);
138error_helper!(ErrorNotAcceptable, NOT_ACCEPTABLE);
139error_helper!(
140 ErrorProxyAuthenticationRequired,
141 PROXY_AUTHENTICATION_REQUIRED
142);
143error_helper!(ErrorRequestTimeout, REQUEST_TIMEOUT);
144error_helper!(ErrorConflict, CONFLICT);
145error_helper!(ErrorGone, GONE);
146error_helper!(ErrorLengthRequired, LENGTH_REQUIRED);
147error_helper!(ErrorPayloadTooLarge, PAYLOAD_TOO_LARGE);
148error_helper!(ErrorUriTooLong, URI_TOO_LONG);
149error_helper!(ErrorUnsupportedMediaType, UNSUPPORTED_MEDIA_TYPE);
150error_helper!(ErrorRangeNotSatisfiable, RANGE_NOT_SATISFIABLE);
151error_helper!(ErrorImATeapot, IM_A_TEAPOT);
152error_helper!(ErrorMisdirectedRequest, MISDIRECTED_REQUEST);
153error_helper!(ErrorUnprocessableEntity, UNPROCESSABLE_ENTITY);
154error_helper!(ErrorLocked, LOCKED);
155error_helper!(ErrorFailedDependency, FAILED_DEPENDENCY);
156error_helper!(ErrorUpgradeRequired, UPGRADE_REQUIRED);
157error_helper!(ErrorPreconditionFailed, PRECONDITION_FAILED);
158error_helper!(ErrorPreconditionRequired, PRECONDITION_REQUIRED);
159error_helper!(ErrorTooManyRequests, TOO_MANY_REQUESTS);
160error_helper!(
161 ErrorRequestHeaderFieldsTooLarge,
162 REQUEST_HEADER_FIELDS_TOO_LARGE
163);
164error_helper!(
165 ErrorUnavailableForLegalReasons,
166 UNAVAILABLE_FOR_LEGAL_REASONS
167);
168error_helper!(ErrorExpectationFailed, EXPECTATION_FAILED);
169error_helper!(ErrorInternalServerError, INTERNAL_SERVER_ERROR);
170error_helper!(ErrorNotImplemented, NOT_IMPLEMENTED);
171error_helper!(ErrorBadGateway, BAD_GATEWAY);
172error_helper!(ErrorServiceUnavailable, SERVICE_UNAVAILABLE);
173error_helper!(ErrorGatewayTimeout, GATEWAY_TIMEOUT);
174error_helper!(ErrorHttpVersionNotSupported, HTTP_VERSION_NOT_SUPPORTED);
175error_helper!(ErrorVariantAlsoNegotiates, VARIANT_ALSO_NEGOTIATES);
176error_helper!(ErrorInsufficientStorage, INSUFFICIENT_STORAGE);
177error_helper!(ErrorLoopDetected, LOOP_DETECTED);
178error_helper!(ErrorNotExtended, NOT_EXTENDED);
179error_helper!(
180 ErrorNetworkAuthenticationRequired,
181 NETWORK_AUTHENTICATION_REQUIRED
182);
183
184#[cfg(test)]
185mod tests {
186 use actix_http::error::ParseError;
187
188 use super::*;
189
190 #[test]
191 fn test_internal_error() {
192 let err = InternalError::from_response(ParseError::Method, HttpResponse::Ok().finish());
193 let resp: HttpResponse = err.error_response();
194 assert_eq!(resp.status(), StatusCode::OK);
195 }
196
197 #[test]
198 fn test_error_helpers() {
199 let res: HttpResponse = ErrorBadRequest("err").into();
200 assert_eq!(res.status(), StatusCode::BAD_REQUEST);
201
202 let res: HttpResponse = ErrorUnauthorized("err").into();
203 assert_eq!(res.status(), StatusCode::UNAUTHORIZED);
204
205 let res: HttpResponse = ErrorPaymentRequired("err").into();
206 assert_eq!(res.status(), StatusCode::PAYMENT_REQUIRED);
207
208 let res: HttpResponse = ErrorForbidden("err").into();
209 assert_eq!(res.status(), StatusCode::FORBIDDEN);
210
211 let res: HttpResponse = ErrorNotFound("err").into();
212 assert_eq!(res.status(), StatusCode::NOT_FOUND);
213
214 let res: HttpResponse = ErrorMethodNotAllowed("err").into();
215 assert_eq!(res.status(), StatusCode::METHOD_NOT_ALLOWED);
216
217 let res: HttpResponse = ErrorNotAcceptable("err").into();
218 assert_eq!(res.status(), StatusCode::NOT_ACCEPTABLE);
219
220 let res: HttpResponse = ErrorProxyAuthenticationRequired("err").into();
221 assert_eq!(res.status(), StatusCode::PROXY_AUTHENTICATION_REQUIRED);
222
223 let res: HttpResponse = ErrorRequestTimeout("err").into();
224 assert_eq!(res.status(), StatusCode::REQUEST_TIMEOUT);
225
226 let res: HttpResponse = ErrorConflict("err").into();
227 assert_eq!(res.status(), StatusCode::CONFLICT);
228
229 let res: HttpResponse = ErrorGone("err").into();
230 assert_eq!(res.status(), StatusCode::GONE);
231
232 let res: HttpResponse = ErrorLengthRequired("err").into();
233 assert_eq!(res.status(), StatusCode::LENGTH_REQUIRED);
234
235 let res: HttpResponse = ErrorPreconditionFailed("err").into();
236 assert_eq!(res.status(), StatusCode::PRECONDITION_FAILED);
237
238 let res: HttpResponse = ErrorPayloadTooLarge("err").into();
239 assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);
240
241 let res: HttpResponse = ErrorUriTooLong("err").into();
242 assert_eq!(res.status(), StatusCode::URI_TOO_LONG);
243
244 let res: HttpResponse = ErrorUnsupportedMediaType("err").into();
245 assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
246
247 let res: HttpResponse = ErrorRangeNotSatisfiable("err").into();
248 assert_eq!(res.status(), StatusCode::RANGE_NOT_SATISFIABLE);
249
250 let res: HttpResponse = ErrorExpectationFailed("err").into();
251 assert_eq!(res.status(), StatusCode::EXPECTATION_FAILED);
252
253 let res: HttpResponse = ErrorImATeapot("err").into();
254 assert_eq!(res.status(), StatusCode::IM_A_TEAPOT);
255
256 let res: HttpResponse = ErrorMisdirectedRequest("err").into();
257 assert_eq!(res.status(), StatusCode::MISDIRECTED_REQUEST);
258
259 let res: HttpResponse = ErrorUnprocessableEntity("err").into();
260 assert_eq!(res.status(), StatusCode::UNPROCESSABLE_ENTITY);
261
262 let res: HttpResponse = ErrorLocked("err").into();
263 assert_eq!(res.status(), StatusCode::LOCKED);
264
265 let res: HttpResponse = ErrorFailedDependency("err").into();
266 assert_eq!(res.status(), StatusCode::FAILED_DEPENDENCY);
267
268 let res: HttpResponse = ErrorUpgradeRequired("err").into();
269 assert_eq!(res.status(), StatusCode::UPGRADE_REQUIRED);
270
271 let res: HttpResponse = ErrorPreconditionRequired("err").into();
272 assert_eq!(res.status(), StatusCode::PRECONDITION_REQUIRED);
273
274 let res: HttpResponse = ErrorTooManyRequests("err").into();
275 assert_eq!(res.status(), StatusCode::TOO_MANY_REQUESTS);
276
277 let res: HttpResponse = ErrorRequestHeaderFieldsTooLarge("err").into();
278 assert_eq!(res.status(), StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE);
279
280 let res: HttpResponse = ErrorUnavailableForLegalReasons("err").into();
281 assert_eq!(res.status(), StatusCode::UNAVAILABLE_FOR_LEGAL_REASONS);
282
283 let res: HttpResponse = ErrorInternalServerError("err").into();
284 assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
285
286 let res: HttpResponse = ErrorNotImplemented("err").into();
287 assert_eq!(res.status(), StatusCode::NOT_IMPLEMENTED);
288
289 let res: HttpResponse = ErrorBadGateway("err").into();
290 assert_eq!(res.status(), StatusCode::BAD_GATEWAY);
291
292 let res: HttpResponse = ErrorServiceUnavailable("err").into();
293 assert_eq!(res.status(), StatusCode::SERVICE_UNAVAILABLE);
294
295 let res: HttpResponse = ErrorGatewayTimeout("err").into();
296 assert_eq!(res.status(), StatusCode::GATEWAY_TIMEOUT);
297
298 let res: HttpResponse = ErrorHttpVersionNotSupported("err").into();
299 assert_eq!(res.status(), StatusCode::HTTP_VERSION_NOT_SUPPORTED);
300
301 let res: HttpResponse = ErrorVariantAlsoNegotiates("err").into();
302 assert_eq!(res.status(), StatusCode::VARIANT_ALSO_NEGOTIATES);
303
304 let res: HttpResponse = ErrorInsufficientStorage("err").into();
305 assert_eq!(res.status(), StatusCode::INSUFFICIENT_STORAGE);
306
307 let res: HttpResponse = ErrorLoopDetected("err").into();
308 assert_eq!(res.status(), StatusCode::LOOP_DETECTED);
309
310 let res: HttpResponse = ErrorNotExtended("err").into();
311 assert_eq!(res.status(), StatusCode::NOT_EXTENDED);
312
313 let res: HttpResponse = ErrorNetworkAuthenticationRequired("err").into();
314 assert_eq!(res.status(), StatusCode::NETWORK_AUTHENTICATION_REQUIRED);
315 }
316}