1use std::{error, fmt, io, io::Write, rc::Rc, string::FromUtf8Error};
3
4use ntex_h2::{self as h2};
5use ntex_http::{StatusCode, header};
6
7pub use crate::channel::Canceled;
9pub use ntex_http::error::Error as HttpError;
10
11use crate::http::body::Body;
12use crate::http::response::Response;
13use crate::util::{BytesMut, Either, clone_io_error};
14
15pub trait ResponseError: error::Error {
17 fn error_response(&self) -> Response {
21 let mut resp = Response::new(StatusCode::INTERNAL_SERVER_ERROR);
22 let mut buf = BytesMut::new();
23 let _ = write!(&mut buf, "{self}");
24 resp.headers_mut().insert(
25 header::CONTENT_TYPE,
26 header::HeaderValue::from_static("text/plain; charset=utf-8"),
27 );
28 resp.set_body(Body::from(buf))
29 }
30}
31
32impl<T: ResponseError> ResponseError for &T {
33 fn error_response(&self) -> Response {
34 (*self).error_response()
35 }
36}
37
38impl<T: ResponseError> From<T> for Response {
39 fn from(err: T) -> Response {
40 let resp = err.error_response();
41 if resp.head().status == StatusCode::INTERNAL_SERVER_ERROR {
42 log::error!("Internal Server Error: {err:?}");
43 } else {
44 log::debug!("Error in response: {err:?}");
45 }
46 resp
47 }
48}
49
50impl ResponseError for HttpError {}
53
54impl ResponseError for io::Error {}
56
57impl ResponseError for serde_json::error::Error {}
59
60#[derive(thiserror::Error, Debug)]
62pub enum EncodeError {
63 #[error("Unsupported HTTP version specified, {0:?}")]
65 UnsupportedVersion(super::Version),
66
67 #[error("Unexpected end of bytes stream")]
68 UnexpectedEof,
69
70 #[error("Formater error")]
72 Fmt(#[source] io::Error),
73}
74
75impl Clone for EncodeError {
76 fn clone(&self) -> Self {
77 match self {
78 EncodeError::UnexpectedEof => EncodeError::UnexpectedEof,
79 EncodeError::UnsupportedVersion(err) => EncodeError::UnsupportedVersion(*err),
80 EncodeError::Fmt(err) => EncodeError::Fmt(clone_io_error(err)),
81 }
82 }
83}
84
85#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, Hash)]
87pub enum DecodeError {
88 #[error("Invalid Method specified")]
90 Method,
91 #[error("Uri error")]
93 Uri,
94 #[error("Invalid HTTP version specified")]
96 Version,
97 #[error("Invalid Header provided")]
99 Header,
100 #[error("Maximum number of headers are received")]
102 MaxHeaders,
103 #[error("Message head is too large")]
105 TooLarge(usize),
106 #[error("Message is incomplete")]
108 Incomplete,
109 #[error("Invalid Status provided")]
111 Status,
112 #[error("`InvalidInput` occurred while trying to parse incoming stream: {0}")]
114 InvalidInput(&'static str),
115 #[error("UTF8 error")]
117 Utf8,
118}
119
120impl From<ntex_http::compat::InvalidUri> for DecodeError {
121 fn from(_: ntex_http::compat::InvalidUri) -> DecodeError {
122 DecodeError::Uri
123 }
124}
125
126impl From<FromUtf8Error> for DecodeError {
127 fn from(_: FromUtf8Error) -> DecodeError {
128 DecodeError::Utf8
129 }
130}
131
132impl From<httparse::Error> for DecodeError {
133 fn from(err: httparse::Error) -> DecodeError {
134 match err {
135 httparse::Error::HeaderName
136 | httparse::Error::HeaderValue
137 | httparse::Error::NewLine
138 | httparse::Error::Token => DecodeError::Header,
139 httparse::Error::Status => DecodeError::Status,
140 httparse::Error::TooManyHeaders => DecodeError::TooLarge(0),
141 httparse::Error::Version => DecodeError::Version,
142 }
143 }
144}
145
146impl From<ntex_httparse::Error> for DecodeError {
147 fn from(err: ntex_httparse::Error) -> DecodeError {
148 match err {
149 ntex_httparse::Error::HeaderName
150 | ntex_httparse::Error::HeaderValue
151 | ntex_httparse::Error::NewLine
152 | ntex_httparse::Error::Token => DecodeError::Header,
153 ntex_httparse::Error::Status => DecodeError::Status,
154 ntex_httparse::Error::TooManyHeaders => DecodeError::TooLarge(0),
155 ntex_httparse::Error::Version => DecodeError::Version,
156 }
157 }
158}
159
160#[derive(thiserror::Error, Debug)]
161pub enum PayloadError {
163 #[error("A payload reached EOF, but is not complete. With error: {0:?}")]
165 Incomplete(#[source] Option<io::Error>),
166 #[error("Cannot decode content-encoding.")]
168 EncodingCorrupted,
169 #[error("A payload reached size limit.")]
171 Overflow,
172 #[error("A payload length is unknown.")]
174 UnknownLength,
175 #[error("")]
177 Http2Payload(
178 #[from]
179 #[source]
180 h2::StreamError,
181 ),
182 #[error("Decode error")]
184 Decode(
185 #[from]
186 #[source]
187 DecodeError,
188 ),
189 #[error("{0}")]
191 Io(
192 #[from]
193 #[source]
194 io::Error,
195 ),
196}
197
198impl Clone for PayloadError {
199 fn clone(&self) -> PayloadError {
200 match self {
201 PayloadError::Incomplete(err) => {
202 PayloadError::Incomplete(err.as_ref().map(clone_io_error))
203 }
204 PayloadError::EncodingCorrupted => PayloadError::EncodingCorrupted,
205 PayloadError::Overflow => PayloadError::Overflow,
206 PayloadError::UnknownLength => PayloadError::UnknownLength,
207 PayloadError::Http2Payload(err) => PayloadError::Http2Payload(*err),
208 PayloadError::Decode(err) => PayloadError::Decode(*err),
209 PayloadError::Io(err) => PayloadError::Io(clone_io_error(err)),
210 }
211 }
212}
213
214impl From<Either<PayloadError, io::Error>> for PayloadError {
215 fn from(err: Either<PayloadError, io::Error>) -> Self {
216 match err {
217 Either::Left(err) => err,
218 Either::Right(err) => PayloadError::Io(err),
219 }
220 }
221}
222
223#[derive(thiserror::Error, Clone, Debug)]
224pub enum DispatchError {
226 #[error("Service error {0}")]
228 Service(Rc<dyn ResponseError>),
229
230 #[error("Control service error: {0}")]
232 Control(#[source] Rc<dyn error::Error>),
233}
234
235#[derive(thiserror::Error, Clone, Debug)]
236pub enum H2Error {
238 #[error("Operation error")]
240 Operation(
241 #[from]
242 #[source]
243 h2::OperationError,
244 ),
245 #[error("Missing pseudo header: {0}")]
247 MissingPseudo(&'static str),
248 #[error("Uri")]
250 Uri,
251 #[error("{0}")]
253 Stream(
254 #[from]
255 #[source]
256 Rc<dyn error::Error>,
257 ),
258}
259
260impl From<ntex_http::compat::InvalidUri> for H2Error {
261 fn from(_: ntex_http::compat::InvalidUri) -> H2Error {
262 H2Error::Uri
263 }
264}
265
266#[derive(thiserror::Error, Copy, Clone, PartialEq, Eq, Hash, Debug)]
268pub enum ContentTypeError {
269 #[error("Cannot parse content type")]
271 ParseError,
272 #[error("Unknown content encoding")]
274 UnknownEncoding,
275 #[error("Unexpected Content-Type")]
277 Unexpected,
278 #[error("Content-Type is expected")]
280 Expected,
281}
282
283#[derive(thiserror::Error, Debug)]
285pub enum BlockingError<E: fmt::Debug> {
286 #[error("{0:?}")]
287 Error(#[source] E),
288 #[error("Thread pool is gone")]
289 Canceled,
290}
291
292impl From<crate::rt::JoinError> for PayloadError {
293 fn from(_: crate::rt::JoinError) -> Self {
294 PayloadError::Io(io::Error::new(
295 io::ErrorKind::Interrupted,
296 "Operation is canceled",
297 ))
298 }
299}
300
301impl From<crate::rt::BlockingError> for PayloadError {
302 fn from(_: crate::rt::BlockingError) -> Self {
303 PayloadError::Io(io::Error::new(
304 io::ErrorKind::Interrupted,
305 "Operation is canceled",
306 ))
307 }
308}
309
310impl From<BlockingError<io::Error>> for PayloadError {
311 fn from(err: BlockingError<io::Error>) -> Self {
312 match err {
313 BlockingError::Error(e) => PayloadError::Io(e),
314 BlockingError::Canceled => PayloadError::Io(io::Error::new(
315 io::ErrorKind::Interrupted,
316 "Operation is canceled",
317 )),
318 }
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use ntex_http::Error as HttpError;
326
327 #[test]
328 fn test_into_response() {
329 let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();
330 let resp: Response = err.error_response();
331 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
332 }
333
334 #[test]
335 fn test_error_http_response() {
336 let orig = io::Error::other("other");
337 let resp: Response = orig.into();
338 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
339 }
340
341 #[test]
342 fn test_payload_error() {
343 let err: PayloadError = io::Error::other("DecodeError").into();
344 assert!(format!("{err}").contains("DecodeError"), "{err}");
345
346 let err: PayloadError = BlockingError::Canceled.into();
347 assert!(format!("{err}").contains("Operation is canceled"), "{err}");
348
349 let err: PayloadError =
350 BlockingError::Error(io::Error::other("DecodeError")).into();
351 assert!(format!("{err}").contains("DecodeError"), "{err}");
352
353 let err = PayloadError::Incomplete(None);
354 assert_eq!(
355 format!("{err}"),
356 "A payload reached EOF, but is not complete. With error: None"
357 );
358 }
359
360 macro_rules! from {
361 ($from:expr => $error:pat) => {
362 match DecodeError::from($from) {
363 e @ $error => {
364 assert!(format!("{e}").len() >= 5);
365 }
366 e => unreachable!("{e:?}"),
367 }
368 };
369 }
370
371 #[test]
372 fn test_from() {
373 from!(httparse::Error::HeaderName => DecodeError::Header);
374 from!(httparse::Error::HeaderName => DecodeError::Header);
375 from!(httparse::Error::HeaderValue => DecodeError::Header);
376 from!(httparse::Error::NewLine => DecodeError::Header);
377 from!(httparse::Error::Status => DecodeError::Status);
378 from!(httparse::Error::Token => DecodeError::Header);
379 from!(httparse::Error::TooManyHeaders => DecodeError::TooLarge(0));
380 from!(httparse::Error::Version => DecodeError::Version);
381 from!(ntex_httparse::Error::HeaderName => DecodeError::Header);
382 from!(ntex_httparse::Error::HeaderName => DecodeError::Header);
383 from!(ntex_httparse::Error::HeaderValue => DecodeError::Header);
384 from!(ntex_httparse::Error::NewLine => DecodeError::Header);
385 from!(ntex_httparse::Error::Status => DecodeError::Status);
386 from!(ntex_httparse::Error::Token => DecodeError::Header);
387 from!(ntex_httparse::Error::TooManyHeaders => DecodeError::TooLarge(0));
388 from!(ntex_httparse::Error::Version => DecodeError::Version);
389 }
390}