Skip to main content

ntex/web/
error_default.rs

1//! Web error
2use std::{fmt, io, io::Write, str::Utf8Error};
3
4use serde::de::value::Error as DeError;
5use serde_json::error::Error as JsonError;
6use serde_urlencoded::ser::Error as FormError;
7
8use crate::client;
9use crate::http::{self, StatusCode, body::Body, header};
10use crate::util::{BytesMut, timeout::TimeoutError};
11#[cfg(feature = "ws")]
12use crate::ws::error::HandshakeError;
13
14use super::error::{self, ErrorContainer, ErrorRenderer, WebResponseError};
15use super::{HttpRequest, HttpResponse};
16
17/// Default error type
18#[derive(Clone, Copy, Default, Debug)]
19pub struct DefaultError;
20
21impl ErrorRenderer for DefaultError {
22    type Container = Error;
23}
24
25/// Generic error container for errors that supports `DefaultError` renderer.
26#[derive(thiserror::Error)]
27pub struct Error {
28    cause: Box<dyn WebResponseError<DefaultError>>,
29}
30
31impl Error {
32    pub fn new<T: WebResponseError<DefaultError> + 'static>(err: T) -> Error {
33        Error {
34            cause: Box::new(err),
35        }
36    }
37
38    /// Returns the reference to the underlying `WebResponseError`.
39    pub fn as_response_error(&self) -> &dyn WebResponseError<DefaultError> {
40        self.cause.as_ref()
41    }
42}
43
44/// `Error` for any error which implements `WebResponseError<DefaultError>`
45impl<T: WebResponseError<DefaultError>> From<T> for Error {
46    fn from(err: T) -> Self {
47        Error {
48            cause: Box::new(err),
49        }
50    }
51}
52
53impl ErrorContainer for Error {
54    fn error_response(&self, req: &HttpRequest) -> HttpResponse {
55        self.cause.error_response(req)
56    }
57}
58
59impl crate::http::error::ResponseError for Error {
60    fn error_response(&self) -> HttpResponse {
61        let mut resp = HttpResponse::new(self.cause.status_code());
62        let mut buf = BytesMut::new();
63        let _ = write!(&mut buf, "{}", self.cause);
64        resp.headers_mut().insert(
65            header::CONTENT_TYPE,
66            header::HeaderValue::from_static("text/plain; charset=utf-8"),
67        );
68        resp.set_body(Body::from(buf))
69    }
70}
71
72impl fmt::Display for Error {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        fmt::Display::fmt(&self.cause, f)
75    }
76}
77
78impl fmt::Debug for Error {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        write!(f, "web::Error({:?})", self.cause)
81    }
82}
83
84/// Return `GATEWAY_TIMEOUT` for `TimeoutError`
85impl<E> From<TimeoutError<E>> for Error
86where
87    Error: From<E>,
88{
89    fn from(err: TimeoutError<E>) -> Error {
90        match err {
91            TimeoutError::Service(e) => e.into(),
92            TimeoutError::Timeout => super::error::ErrorGatewayTimeout("").into(),
93        }
94    }
95}
96
97/// `InternalServerError` for `StateExtractorError`
98impl WebResponseError<DefaultError> for error::StateExtractorError {}
99
100/// `InternalServerError` for `JsonError`
101impl WebResponseError<DefaultError> for JsonError {}
102
103/// `InternalServerError` for `FormError`
104impl WebResponseError<DefaultError> for FormError {}
105
106#[cfg(feature = "openssl")]
107/// `InternalServerError` for `openssl::ssl::Error`
108impl WebResponseError<DefaultError> for tls_openssl::ssl::Error {}
109
110#[cfg(feature = "openssl")]
111/// `InternalServerError` for `openssl::ssl::HandshakeError`
112impl<T: fmt::Debug + 'static> WebResponseError<DefaultError>
113    for tls_openssl::ssl::HandshakeError<T>
114{
115}
116
117/// Return `BAD_REQUEST` for `de::value::Error`
118impl WebResponseError<DefaultError> for DeError {
119    fn status_code(&self) -> StatusCode {
120        StatusCode::BAD_REQUEST
121    }
122}
123
124/// `InternalServerError` for `Canceled`
125impl WebResponseError<DefaultError> for crate::http::error::Canceled {}
126
127/// `InternalServerError` for `BlockingError`
128impl<E: fmt::Debug + 'static> WebResponseError<DefaultError>
129    for crate::http::error::BlockingError<E>
130{
131}
132
133/// Return `BAD_REQUEST` for `Utf8Error`
134impl WebResponseError<DefaultError> for Utf8Error {
135    fn status_code(&self) -> StatusCode {
136        StatusCode::BAD_REQUEST
137    }
138}
139
140/// Return `InternalServerError` for `HttpError`,
141/// Response generation can return `HttpError`, so it is internal error
142impl WebResponseError<DefaultError> for crate::http::error::HttpError {}
143
144/// Return `InternalServerError` for `io::Error`
145impl WebResponseError<DefaultError> for io::Error {
146    fn status_code(&self) -> StatusCode {
147        match self.kind() {
148            io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
149            io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
150            _ => StatusCode::INTERNAL_SERVER_ERROR,
151        }
152    }
153}
154
155/// `InternalServerError` for `UrlGeneratorError`
156impl WebResponseError<DefaultError> for error::UrlGenerationError {}
157
158/// Response renderer for `UrlencodedError`
159impl WebResponseError<DefaultError> for error::UrlencodedError {
160    fn status_code(&self) -> StatusCode {
161        match *self {
162            error::UrlencodedError::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
163            error::UrlencodedError::UnknownLength => StatusCode::LENGTH_REQUIRED,
164            _ => StatusCode::BAD_REQUEST,
165        }
166    }
167}
168
169/// Return `BadRequest` for `JsonPayloadError`
170impl WebResponseError<DefaultError> for error::JsonPayloadError {
171    fn status_code(&self) -> StatusCode {
172        match *self {
173            error::JsonPayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
174            _ => StatusCode::BAD_REQUEST,
175        }
176    }
177}
178
179/// Error renderer for `PathError`
180impl WebResponseError<DefaultError> for error::PathError {
181    fn status_code(&self) -> StatusCode {
182        StatusCode::NOT_FOUND
183    }
184}
185
186/// Error renderer `QueryPayloadError`
187impl WebResponseError<DefaultError> for error::QueryPayloadError {
188    fn status_code(&self) -> StatusCode {
189        StatusCode::BAD_REQUEST
190    }
191}
192
193impl WebResponseError<DefaultError> for error::PayloadError {
194    fn status_code(&self) -> StatusCode {
195        StatusCode::BAD_REQUEST
196    }
197}
198
199/// `PayloadError` returns two possible results:
200///
201/// - `Overflow` returns `PayloadTooLarge`
202/// - Other errors returns `BadRequest`
203impl WebResponseError<DefaultError> for http::error::PayloadError {
204    fn status_code(&self) -> StatusCode {
205        match *self {
206            http::error::PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
207            _ => StatusCode::BAD_REQUEST,
208        }
209    }
210}
211
212#[cfg(feature = "cookie")]
213/// Return `BadRequest` for `cookie::ParseError`
214impl WebResponseError<DefaultError> for coo_kie::ParseError {
215    fn status_code(&self) -> StatusCode {
216        StatusCode::BAD_REQUEST
217    }
218}
219
220/// Return `BadRequest` for `ContentTypeError`
221impl WebResponseError<DefaultError> for http::error::ContentTypeError {
222    fn status_code(&self) -> StatusCode {
223        StatusCode::BAD_REQUEST
224    }
225}
226
227/// Convert `ClientError` to a server `Response`
228impl WebResponseError<DefaultError> for client::error::ClientError {
229    fn status_code(&self) -> StatusCode {
230        match self {
231            client::error::ClientError::Connect(err) => {
232                if matches!(err, client::error::ConnectError::Timeout) {
233                    StatusCode::GATEWAY_TIMEOUT
234                } else {
235                    StatusCode::BAD_REQUEST
236                }
237            }
238            _ => StatusCode::INTERNAL_SERVER_ERROR,
239        }
240    }
241}
242
243#[cfg(feature = "ws")]
244/// Error renderer for `ws::HandshakeError`
245impl WebResponseError<DefaultError> for HandshakeError {
246    fn error_response(&self, _: &HttpRequest) -> HttpResponse {
247        match *self {
248            HandshakeError::GetMethodRequired => HttpResponse::MethodNotAllowed()
249                .header(header::ALLOW, "GET")
250                .finish(),
251            HandshakeError::NoWebsocketUpgrade => HttpResponse::BadRequest()
252                .reason("No WebSocket UPGRADE header found")
253                .finish(),
254            HandshakeError::NoConnectionUpgrade => HttpResponse::BadRequest()
255                .reason("No CONNECTION upgrade")
256                .finish(),
257            HandshakeError::NoVersionHeader => HttpResponse::BadRequest()
258                .reason("Websocket version header is required")
259                .finish(),
260            HandshakeError::UnsupportedVersion => HttpResponse::BadRequest()
261                .reason("Unsupported version")
262                .finish(),
263            HandshakeError::BadWebsocketKey => HttpResponse::BadRequest()
264                .reason("Handshake error")
265                .finish(),
266        }
267    }
268}