1use std::{error, fmt, io, io::Write, 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::error::Failure;
12use crate::http::{body::Body, response::Response};
13use crate::util::{BytesMut, Either, clone_io_error};
14
15pub trait ResponseError: error::Error {
17 fn error_response(&self) -> Response {
24 let mut resp = Response::new(StatusCode::INTERNAL_SERVER_ERROR);
25 let mut buf = BytesMut::new();
26 let _ = write!(&mut buf, "{self}");
27 resp.headers_mut().insert(
28 header::CONTENT_TYPE,
29 header::HeaderValue::from_static("text/plain; charset=utf-8"),
30 );
31 resp.set_body(Body::from(buf))
32 }
33}
34
35impl<T: ResponseError> ResponseError for &T {
36 fn error_response(&self) -> Response {
37 (*self).error_response()
38 }
39}
40
41impl<T: ResponseError> From<T> for Response {
42 fn from(err: T) -> Response {
43 let resp = err.error_response();
44 if resp.head().status == StatusCode::INTERNAL_SERVER_ERROR {
45 log::error!("Internal Server Error: {err:?}");
46 } else {
47 log::debug!("Error in response: {err:?}");
48 }
49 resp
50 }
51}
52
53impl ResponseError for HttpError {}
56
57impl ResponseError for io::Error {}
59
60impl ResponseError for serde_json::error::Error {}
62
63#[derive(thiserror::Error, Clone, Debug)]
65pub enum EncodeError {
66 #[error("Unsupported HTTP version specified, {0:?}")]
68 UnsupportedVersion(super::Version),
69
70 #[error("Unexpected end of bytes stream")]
72 UnexpectedEof,
73
74 #[error("Request body length is unknown")]
76 UnknownLength,
77}
78
79#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, Hash)]
81pub enum DecodeError {
82 #[error("Invalid Method specified")]
84 Method,
85 #[error("Uri error")]
87 Uri,
88 #[error("Invalid HTTP version specified")]
90 Version,
91 #[error("Invalid Header provided")]
93 Header,
94 #[error("Maximum number of headers are received")]
96 MaxHeaders,
97 #[error("Message head is too large")]
99 TooLarge(usize),
100 #[error("Request or status line is too long")]
102 StartLineTooLong(usize),
103 #[error("Unsupported transfer coding")]
105 UnsupportedTransferCoding,
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<urly::InvalidUrl> for DecodeError {
121 fn from(_: urly::InvalidUrl) -> 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(Failure),
229
230 #[error("Control service error")]
232 Control(Failure),
233}
234
235#[derive(thiserror::Error, Clone, Debug)]
236pub enum H2Error {
238 #[error("Too many consecutive empty DATA frames")]
240 EmptyDataFrames,
241}
242
243impl crate::error::ErrorDiagnostic for H2Error {
244 fn signature(&self) -> &'static str {
245 "ntex-http-h2error"
246 }
247}
248
249#[derive(thiserror::Error, Copy, Clone, PartialEq, Eq, Hash, Debug)]
251pub enum ContentTypeError {
252 #[error("Cannot parse content type")]
254 ParseError,
255 #[error("Unknown content encoding")]
257 UnknownEncoding,
258 #[error("Unexpected Content-Type")]
260 Unexpected,
261 #[error("Content-Type is expected")]
263 Expected,
264}
265
266#[derive(thiserror::Error, Debug)]
268pub enum BlockingError<E: fmt::Debug> {
269 #[error("{0:?}")]
271 Error(#[source] E),
272 #[error("Thread pool is gone")]
274 Canceled,
275}
276
277impl From<crate::rt::JoinError> for PayloadError {
278 fn from(_: crate::rt::JoinError) -> Self {
279 PayloadError::Io(io::Error::new(
280 io::ErrorKind::Interrupted,
281 "Operation is canceled",
282 ))
283 }
284}
285
286impl From<crate::rt::BlockingError> for PayloadError {
287 fn from(_: crate::rt::BlockingError) -> Self {
288 PayloadError::Io(io::Error::new(
289 io::ErrorKind::Interrupted,
290 "Operation is canceled",
291 ))
292 }
293}
294
295impl From<BlockingError<io::Error>> for PayloadError {
296 fn from(err: BlockingError<io::Error>) -> Self {
297 match err {
298 BlockingError::Error(e) => PayloadError::Io(e),
299 BlockingError::Canceled => PayloadError::Io(io::Error::new(
300 io::ErrorKind::Interrupted,
301 "Operation is canceled",
302 )),
303 }
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use ntex_http::Error as HttpError;
311
312 #[test]
313 fn test_into_response() {
314 let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();
315 let resp: Response = err.error_response();
316 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
317 }
318
319 #[test]
320 fn test_error_http_response() {
321 let orig = io::Error::other("other");
322 let resp: Response = orig.into();
323 assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
324 }
325
326 #[test]
327 fn test_payload_error() {
328 let err: PayloadError = io::Error::other("DecodeError").into();
329 assert!(format!("{err}").contains("DecodeError"), "{err}");
330
331 let err: PayloadError = BlockingError::Canceled.into();
332 assert!(format!("{err}").contains("Operation is canceled"), "{err}");
333
334 let err: PayloadError = BlockingError::Error(io::Error::other("DecodeError")).into();
335 assert!(format!("{err}").contains("DecodeError"), "{err}");
336
337 let err = PayloadError::Incomplete(None);
338 assert_eq!(
339 format!("{err}"),
340 "A payload reached EOF, but is not complete. With error: None"
341 );
342 }
343
344 macro_rules! from {
345 ($from:expr => $error:pat) => {
346 match DecodeError::from($from) {
347 e @ $error => {
348 assert!(format!("{e}").len() >= 5);
349 }
350 e => unreachable!("{e:?}"),
351 }
352 };
353 }
354
355 #[test]
356 fn test_from() {
357 from!(httparse::Error::HeaderName => DecodeError::Header);
358 from!(httparse::Error::HeaderName => DecodeError::Header);
359 from!(httparse::Error::HeaderValue => DecodeError::Header);
360 from!(httparse::Error::NewLine => DecodeError::Header);
361 from!(httparse::Error::Status => DecodeError::Status);
362 from!(httparse::Error::Token => DecodeError::Header);
363 from!(httparse::Error::TooManyHeaders => DecodeError::TooLarge(0));
364 from!(httparse::Error::Version => DecodeError::Version);
365 from!(ntex_httparse::Error::HeaderName => DecodeError::Header);
366 from!(ntex_httparse::Error::HeaderName => DecodeError::Header);
367 from!(ntex_httparse::Error::HeaderValue => DecodeError::Header);
368 from!(ntex_httparse::Error::NewLine => DecodeError::Header);
369 from!(ntex_httparse::Error::Status => DecodeError::Status);
370 from!(ntex_httparse::Error::Token => DecodeError::Header);
371 from!(ntex_httparse::Error::TooManyHeaders => DecodeError::TooLarge(0));
372 from!(ntex_httparse::Error::Version => DecodeError::Version);
373 }
374
375 #[test]
376 fn test_decode_error_from() {
377 let err = "".parse::<urly::Url>().unwrap_err();
378 assert!(matches!(DecodeError::from(err), DecodeError::Uri));
379 let err = String::from_utf8(vec![0xff]).unwrap_err();
380 assert!(matches!(DecodeError::from(err), DecodeError::Utf8));
381 }
382
383 #[test]
384 fn test_payload_error_clone() {
385 let errs = [
386 PayloadError::Incomplete(None),
387 PayloadError::Incomplete(Some(io::Error::other("inc"))),
388 PayloadError::EncodingCorrupted,
389 PayloadError::Overflow,
390 PayloadError::UnknownLength,
391 PayloadError::Http2Payload(ntex_h2::StreamError::Closed),
392 PayloadError::Decode(DecodeError::Method),
393 PayloadError::Io(io::Error::other("io")),
394 ];
395 for err in &errs {
396 assert_eq!(err.to_string(), err.clone().to_string());
397 }
398 }
399
400 #[test]
401 fn test_payload_error_conversions() {
402 let err = PayloadError::from(Either::<PayloadError, io::Error>::Left(
403 PayloadError::Overflow,
404 ));
405 assert!(matches!(err, PayloadError::Overflow));
406 let err = PayloadError::from(Either::<PayloadError, io::Error>::Right(io::Error::other(
407 "right",
408 )));
409 assert!(matches!(err, PayloadError::Io(ref e) if e.to_string() == "right"));
410
411 for err in [
412 PayloadError::from(crate::rt::JoinError),
413 PayloadError::from(crate::rt::BlockingError),
414 ] {
415 assert!(
416 matches!(err, PayloadError::Io(ref e) if e.kind() == io::ErrorKind::Interrupted)
417 );
418 }
419 }
420
421 #[test]
422 fn test_h2_error() {
423 use crate::error::ErrorDiagnostic;
424
425 let err = H2Error::EmptyDataFrames;
426 assert_eq!(err.signature(), "ntex-http-h2error");
427 assert_eq!(err.to_string(), "Too many consecutive empty DATA frames");
428 }
429}