Skip to main content

ntex/http/
error.rs

1//! Http related errors
2use std::{error, fmt, io, io::Write, rc::Rc, string::FromUtf8Error};
3
4use ntex_h2::{self as h2};
5use ntex_http::{StatusCode, header};
6
7// re-export for convinience
8pub 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
15/// Error that can be converted to `Response`
16pub trait ResponseError: error::Error {
17    /// Create response for error
18    ///
19    /// Internal server error is generated by default.
20    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
50/// Return `InternalServerError` for `HttpError`,
51/// Response generation can return `HttpError`, so it is internal error
52impl ResponseError for HttpError {}
53
54/// Return `InternalServerError` for `io::Error`
55impl ResponseError for io::Error {}
56
57/// `InternalServerError` for `JsonError`
58impl ResponseError for serde_json::error::Error {}
59
60/// A set of errors that can occur during HTTP streams encoding
61#[derive(thiserror::Error, Debug)]
62pub enum EncodeError {
63    /// An invalid `HttpVersion`, such as `HTP/1.1`
64    #[error("Unsupported HTTP version specified, {0:?}")]
65    UnsupportedVersion(super::Version),
66
67    #[error("Unexpected end of bytes stream")]
68    UnexpectedEof,
69
70    /// Internal error
71    #[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/// A set of errors that can occur during parsing HTTP streams
86#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, Hash)]
87pub enum DecodeError {
88    /// An invalid `Method`, such as `GE.T`.
89    #[error("Invalid Method specified")]
90    Method,
91    /// An invalid `Uri`, such as `exam ple.domain`.
92    #[error("Uri error")]
93    Uri,
94    /// An invalid `HttpVersion`, such as `HTP/1.1`
95    #[error("Invalid HTTP version specified")]
96    Version,
97    /// An invalid `Header`.
98    #[error("Invalid Header provided")]
99    Header,
100    /// Maximum number of headers are received.
101    #[error("Maximum number of headers are received")]
102    MaxHeaders,
103    /// A message head is too large to be reasonable.
104    #[error("Message head is too large")]
105    TooLarge(usize),
106    /// A message reached EOF, but is not complete.
107    #[error("Message is incomplete")]
108    Incomplete,
109    /// An invalid `Status`, such as `1337 ELITE`.
110    #[error("Invalid Status provided")]
111    Status,
112    /// An `InvalidInput` occurred while trying to parse incoming stream.
113    #[error("`InvalidInput` occurred while trying to parse incoming stream: {0}")]
114    InvalidInput(&'static str),
115    /// Parsing a field as string failed
116    #[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)]
161/// A set of errors that can occur during payload parsing
162pub enum PayloadError {
163    /// A payload reached EOF, but is not complete.
164    #[error("A payload reached EOF, but is not complete. With error: {0:?}")]
165    Incomplete(#[source] Option<io::Error>),
166    /// Content encoding stream corruption
167    #[error("Cannot decode content-encoding.")]
168    EncodingCorrupted,
169    /// A payload reached size limit.
170    #[error("A payload reached size limit.")]
171    Overflow,
172    /// A payload length is unknown.
173    #[error("A payload length is unknown.")]
174    UnknownLength,
175    /// Http2 payload error
176    #[error("")]
177    Http2Payload(
178        #[from]
179        #[source]
180        h2::StreamError,
181    ),
182    /// Decode error
183    #[error("Decode error")]
184    Decode(
185        #[from]
186        #[source]
187        DecodeError,
188    ),
189    /// Io error
190    #[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)]
224/// A set of errors that can occur during dispatching http requests
225pub enum DispatchError {
226    /// Service error
227    #[error("Service error {0}")]
228    Service(Rc<dyn ResponseError>),
229
230    /// Control service error
231    #[error("Control service error: {0}")]
232    Control(#[source] Rc<dyn error::Error>),
233}
234
235#[derive(thiserror::Error, Clone, Debug)]
236/// A set of errors that can occur during dispatching http2 requests
237pub enum H2Error {
238    /// Operation error
239    #[error("Operation error")]
240    Operation(
241        #[from]
242        #[source]
243        h2::OperationError,
244    ),
245    /// Pseudo headers error
246    #[error("Missing pseudo header: {0}")]
247    MissingPseudo(&'static str),
248    /// Uri parsing error
249    #[error("Uri")]
250    Uri,
251    /// Body stream error
252    #[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/// A set of error that can occure during parsing content type
267#[derive(thiserror::Error, Copy, Clone, PartialEq, Eq, Hash, Debug)]
268pub enum ContentTypeError {
269    /// Cannot parse content type
270    #[error("Cannot parse content type")]
271    ParseError,
272    /// Unknown content encoding
273    #[error("Unknown content encoding")]
274    UnknownEncoding,
275    /// Unexpected Content-Type
276    #[error("Unexpected Content-Type")]
277    Unexpected,
278    /// Content-Type is expected
279    #[error("Content-Type is expected")]
280    Expected,
281}
282
283/// Blocking operation execution error
284#[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}