Skip to main content

ntex/web/
error_default.rs

1//! Web error
2use std::{error::Error, fmt, io, 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, header};
10use crate::util::timeout::TimeoutError;
11#[cfg(feature = "ws")]
12use crate::ws::error::HandshakeError;
13
14use super::{HttpResponse, WebResponseError, error};
15
16// =========== DefaultError marker ============
17
18/// Default web error domain.
19///
20/// `DefaultError` is a marker type used as [`State::Error`](crate::web::State::Error).
21/// It selects the built-in renderers for ntex errors: request data errors are
22/// rendered as `4xx` responses and server-side failures as `5xx` responses,
23/// with the error's `Display` text as a plain-text body. The marker itself is
24/// never constructed.
25#[derive(Debug, thiserror::Error)]
26#[error("Default error marker")]
27pub struct DefaultError {
28    _ph: io::Error,
29}
30
31impl<St> WebResponseError<St, DefaultError> for DefaultError {
32    fn error_response(&self, _: &St) -> HttpResponse {
33        unreachable!()
34    }
35}
36
37// =========== DefaultError impls =========
38
39/// `InternalServerError` for `JsonError`.
40///
41/// Serialization errors are server-side failures, such as a `Json<T>` or
42/// `Form<T>` responder failing to encode its value.
43impl<St> WebResponseError<St, DefaultError> for JsonError {
44    fn error_response(&self, _: &St) -> HttpResponse {
45        HttpResponse::render_with(StatusCode::INTERNAL_SERVER_ERROR, self)
46    }
47}
48
49/// `InternalServerError` for `FormError`.
50///
51/// Serialization errors are server-side failures, such as a `Json<T>` or
52/// `Form<T>` responder failing to encode its value.
53impl<St> WebResponseError<St, DefaultError> for FormError {
54    fn error_response(&self, _: &St) -> HttpResponse {
55        HttpResponse::render_with(StatusCode::INTERNAL_SERVER_ERROR, self)
56    }
57}
58
59#[cfg(feature = "openssl")]
60/// `BadRequest` for `openssl::ssl::Error`
61impl<St> WebResponseError<St, DefaultError> for tls_openssl::ssl::Error {
62    fn error_response(&self, _: &St) -> HttpResponse {
63        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
64    }
65}
66
67#[cfg(feature = "openssl")]
68/// `InternalServerError` for `openssl::ssl::HandshakeError`
69impl<St, T: fmt::Debug + 'static> WebResponseError<St, DefaultError>
70    for tls_openssl::ssl::HandshakeError<T>
71{
72}
73
74/// Return `BAD_REQUEST` for `de::value::Error`
75impl<St> WebResponseError<St, DefaultError> for DeError {
76    fn error_response(&self, _: &St) -> HttpResponse {
77        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
78    }
79}
80
81/// `InternalServerError` for `Canceled`
82impl<St> WebResponseError<St, DefaultError> for crate::http::error::Canceled {
83    fn error_response(&self, _: &St) -> HttpResponse {
84        HttpResponse::render_with(StatusCode::INTERNAL_SERVER_ERROR, self)
85    }
86}
87
88/// `InternalServerError` for `BlockingError`
89impl<St, E: Error + 'static> WebResponseError<St, DefaultError>
90    for crate::http::error::BlockingError<E>
91{
92    fn error_response(&self, _: &St) -> HttpResponse {
93        HttpResponse::render_with(StatusCode::INTERNAL_SERVER_ERROR, self)
94    }
95}
96
97/// Return `BAD_REQUEST` for `Utf8Error`
98impl<St> WebResponseError<St, DefaultError> for Utf8Error {
99    fn error_response(&self, _: &St) -> HttpResponse {
100        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
101    }
102}
103
104/// Return `InternalServerError` for `HttpError`,
105/// Response generation can return `HttpError`, so it is internal error
106impl<St> WebResponseError<St, DefaultError> for crate::http::error::HttpError {
107    fn error_response(&self, _: &St) -> HttpResponse {
108        HttpResponse::render_with(StatusCode::INTERNAL_SERVER_ERROR, self)
109    }
110}
111
112/// Return `InternalServerError` for `io::Error`
113impl<St> WebResponseError<St, DefaultError> for io::Error {
114    fn error_response(&self, _: &St) -> HttpResponse {
115        let status = match self.kind() {
116            io::ErrorKind::NotFound => StatusCode::NOT_FOUND,
117            io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN,
118            _ => StatusCode::INTERNAL_SERVER_ERROR,
119        };
120        HttpResponse::render_with(status, self)
121    }
122}
123
124/// `InternalServerError` for `UrlGenerationError`
125impl<St> WebResponseError<St, DefaultError> for error::UrlGenerationError {
126    fn error_response(&self, _: &St) -> HttpResponse {
127        HttpResponse::render_with(StatusCode::INTERNAL_SERVER_ERROR, self)
128    }
129}
130
131/// Response renderer for `UrlencodedError`
132impl<St> WebResponseError<St, DefaultError> for error::UrlencodedError {
133    fn error_response(&self, _: &St) -> HttpResponse {
134        let status = match self {
135            error::UrlencodedError::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
136            error::UrlencodedError::UnknownLength => StatusCode::LENGTH_REQUIRED,
137            _ => StatusCode::BAD_REQUEST,
138        };
139        HttpResponse::render_with(status, self)
140    }
141}
142
143/// Response renderer for `JsonPayloadError`
144///
145/// Returns `PayloadTooLarge` for `Overflow` and `BadRequest` otherwise.
146impl<St> WebResponseError<St, DefaultError> for error::JsonPayloadError {
147    fn error_response(&self, _: &St) -> HttpResponse {
148        let status = match self {
149            error::JsonPayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
150            _ => StatusCode::BAD_REQUEST,
151        };
152        HttpResponse::render_with(status, self)
153    }
154}
155
156/// Error renderer for `PathError`
157impl<St> WebResponseError<St, DefaultError> for error::PathError {
158    fn error_response(&self, _: &St) -> HttpResponse {
159        HttpResponse::render_with(StatusCode::NOT_FOUND, self)
160    }
161}
162
163/// Error renderer `QueryPayloadError`
164impl<St> WebResponseError<St, DefaultError> for error::QueryPayloadError {
165    fn error_response(&self, _: &St) -> HttpResponse {
166        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
167    }
168}
169
170impl<St> WebResponseError<St, DefaultError> for error::PayloadError {
171    fn error_response(&self, _: &St) -> HttpResponse {
172        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
173    }
174}
175
176/// `PayloadError` returns two possible results:
177///
178/// - `Overflow` returns `PayloadTooLarge`
179/// - Other errors returns `BadRequest`
180impl<St> WebResponseError<St, DefaultError> for http::error::PayloadError {
181    fn error_response(&self, _: &St) -> HttpResponse {
182        let status = match self {
183            http::error::PayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
184            _ => StatusCode::BAD_REQUEST,
185        };
186        HttpResponse::render_with(status, self)
187    }
188}
189
190#[cfg(feature = "cookie")]
191/// Return `BadRequest` for `cookie::ParseError`
192impl<St> WebResponseError<St, DefaultError> for coo_kie::ParseError {
193    fn error_response(&self, _: &St) -> HttpResponse {
194        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
195    }
196}
197
198/// Return `BadRequest` for `ContentTypeError`
199impl<St> WebResponseError<St, DefaultError> for http::error::ContentTypeError {
200    fn error_response(&self, _: &St) -> HttpResponse {
201        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
202    }
203}
204
205/// Convert `ClientError` to a server `Response`
206impl<St> WebResponseError<St, DefaultError> for client::error::ClientError {
207    fn error_response(&self, _: &St) -> HttpResponse {
208        let status = match &self {
209            client::error::ClientError::Connect(_) => StatusCode::BAD_REQUEST,
210            client::error::ClientError::Timeout => StatusCode::GATEWAY_TIMEOUT,
211            _ => StatusCode::INTERNAL_SERVER_ERROR,
212        };
213
214        HttpResponse::render_with(status, self)
215    }
216}
217
218/// Convert `ClientPayloadError` to a server `Response`
219impl<St> WebResponseError<St, DefaultError> for client::error::ClientPayloadError {
220    fn error_response(&self, _: &St) -> HttpResponse {
221        HttpResponse::render_with(StatusCode::BAD_REQUEST, self)
222    }
223}
224
225#[cfg(feature = "ws")]
226/// Error renderer for `ws::HandshakeError`
227impl<St> WebResponseError<St, DefaultError> for HandshakeError {
228    fn error_response(&self, _: &St) -> HttpResponse {
229        match self {
230            HandshakeError::GetMethodRequired => HttpResponse::MethodNotAllowed()
231                .header(header::ALLOW, "GET")
232                .build(),
233            HandshakeError::NoWebsocketUpgrade => HttpResponse::BadRequest()
234                .reason("No WebSocket UPGRADE header found")
235                .build(),
236            HandshakeError::NoConnectionUpgrade => HttpResponse::BadRequest()
237                .reason("No CONNECTION upgrade")
238                .build(),
239            HandshakeError::NoVersionHeader => HttpResponse::BadRequest()
240                .reason("Websocket version header is required")
241                .header(header::SEC_WEBSOCKET_VERSION, "13")
242                .build(),
243            HandshakeError::UnsupportedVersion => HttpResponse::BadRequest()
244                .reason("Unsupported version")
245                .header(header::SEC_WEBSOCKET_VERSION, "13")
246                .build(),
247            HandshakeError::BadWebsocketKey => {
248                HttpResponse::BadRequest().reason("Handshake error").build()
249            }
250            HandshakeError::BadWebsocketProtocol => HttpResponse::BadRequest()
251                .reason("Invalid websocket subprotocol")
252                .build(),
253        }
254    }
255}
256
257/// Return `GATEWAY_TIMEOUT` for `TimeoutError`
258impl<St, E> WebResponseError<St, DefaultError> for TimeoutError<E>
259where
260    E: fmt::Display + fmt::Debug + WebResponseError<St, DefaultError> + 'static,
261{
262    fn error_response(&self, st: &St) -> HttpResponse {
263        match self {
264            TimeoutError::Service(e) => e.error_response(st),
265            TimeoutError::Timeout => WebResponseError::<St, DefaultError>::error_response(
266                &super::error::ErrorGatewayTimeout(""),
267                st,
268            ),
269        }
270    }
271}