actix_http/
error.rs

1//! Error and Result module
2
3use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error};
4
5use derive_more::{Display, Error, From};
6pub use http::{status::InvalidStatusCode, Error as HttpError};
7use http::{uri::InvalidUri, StatusCode};
8
9use crate::{body::BoxBody, Response};
10
11pub struct Error {
12    inner: Box<ErrorInner>,
13}
14
15pub(crate) struct ErrorInner {
16    #[allow(dead_code)]
17    kind: Kind,
18    cause: Option<Box<dyn StdError>>,
19}
20
21impl Error {
22    fn new(kind: Kind) -> Self {
23        Self {
24            inner: Box::new(ErrorInner { kind, cause: None }),
25        }
26    }
27
28    pub(crate) fn with_cause(mut self, cause: impl Into<Box<dyn StdError>>) -> Self {
29        self.inner.cause = Some(cause.into());
30        self
31    }
32
33    pub(crate) fn new_http() -> Self {
34        Self::new(Kind::Http)
35    }
36
37    pub(crate) fn new_parse() -> Self {
38        Self::new(Kind::Parse)
39    }
40
41    pub(crate) fn new_payload() -> Self {
42        Self::new(Kind::Payload)
43    }
44
45    pub(crate) fn new_body() -> Self {
46        Self::new(Kind::Body)
47    }
48
49    pub(crate) fn new_send_response() -> Self {
50        Self::new(Kind::SendResponse)
51    }
52
53    #[allow(unused)] // available for future use
54    pub(crate) fn new_io() -> Self {
55        Self::new(Kind::Io)
56    }
57
58    #[allow(unused)] // used in encoder behind feature flag so ignore unused warning
59    pub(crate) fn new_encoder() -> Self {
60        Self::new(Kind::Encoder)
61    }
62
63    #[allow(unused)] // used with `ws` feature flag
64    pub(crate) fn new_ws() -> Self {
65        Self::new(Kind::Ws)
66    }
67}
68
69impl From<Error> for Response<BoxBody> {
70    fn from(err: Error) -> Self {
71        // TODO: more appropriate error status codes, usage assessment needed
72        let status_code = match err.inner.kind {
73            Kind::Parse => StatusCode::BAD_REQUEST,
74            _ => StatusCode::INTERNAL_SERVER_ERROR,
75        };
76
77        Response::new(status_code).set_body(BoxBody::new(err.to_string()))
78    }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)]
82pub(crate) enum Kind {
83    #[display("error processing HTTP")]
84    Http,
85
86    #[display("error parsing HTTP message")]
87    Parse,
88
89    #[display("request payload read error")]
90    Payload,
91
92    #[display("response body write error")]
93    Body,
94
95    #[display("send response error")]
96    SendResponse,
97
98    #[display("error in WebSocket process")]
99    Ws,
100
101    #[display("connection error")]
102    Io,
103
104    #[display("encoder error")]
105    Encoder,
106}
107
108impl fmt::Debug for Error {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        f.debug_struct("actix_http::Error")
111            .field("kind", &self.inner.kind)
112            .field("cause", &self.inner.cause)
113            .finish()
114    }
115}
116
117impl fmt::Display for Error {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        match self.inner.cause.as_ref() {
120            Some(err) => write!(f, "{}: {}", &self.inner.kind, err),
121            None => write!(f, "{}", &self.inner.kind),
122        }
123    }
124}
125
126impl StdError for Error {
127    fn source(&self) -> Option<&(dyn StdError + 'static)> {
128        self.inner.cause.as_ref().map(Box::as_ref)
129    }
130}
131
132impl From<std::convert::Infallible> for Error {
133    fn from(err: std::convert::Infallible) -> Self {
134        match err {}
135    }
136}
137
138impl From<HttpError> for Error {
139    fn from(err: HttpError) -> Self {
140        Self::new_http().with_cause(err)
141    }
142}
143
144#[cfg(feature = "ws")]
145impl From<crate::ws::HandshakeError> for Error {
146    fn from(err: crate::ws::HandshakeError) -> Self {
147        Self::new_ws().with_cause(err)
148    }
149}
150
151#[cfg(feature = "ws")]
152impl From<crate::ws::ProtocolError> for Error {
153    fn from(err: crate::ws::ProtocolError) -> Self {
154        Self::new_ws().with_cause(err)
155    }
156}
157
158/// A set of errors that can occur during parsing HTTP streams.
159#[derive(Debug, Display, Error)]
160#[non_exhaustive]
161pub enum ParseError {
162    /// An invalid `Method`, such as `GE.T`.
163    #[display("invalid method specified")]
164    Method,
165
166    /// An invalid `Uri`, such as `exam ple.domain`.
167    #[display("URI error: {}", _0)]
168    Uri(InvalidUri),
169
170    /// An invalid `HttpVersion`, such as `HTP/1.1`
171    #[display("invalid HTTP version specified")]
172    Version,
173
174    /// An invalid `Header`.
175    #[display("invalid Header provided")]
176    Header,
177
178    /// A message head is too large to be reasonable.
179    #[display("message head is too large")]
180    TooLarge,
181
182    /// A message reached EOF, but is not complete.
183    #[display("message is incomplete")]
184    Incomplete,
185
186    /// An invalid `Status`, such as `1337 ELITE`.
187    #[display("invalid status provided")]
188    Status,
189
190    /// A timeout occurred waiting for an IO event.
191    #[allow(dead_code)]
192    #[display("timeout")]
193    Timeout,
194
195    /// An I/O error that occurred while trying to read or write to a network stream.
196    #[display("I/O error: {}", _0)]
197    Io(io::Error),
198
199    /// Parsing a field as string failed.
200    #[display("UTF-8 error: {}", _0)]
201    Utf8(Utf8Error),
202}
203
204impl From<io::Error> for ParseError {
205    fn from(err: io::Error) -> ParseError {
206        ParseError::Io(err)
207    }
208}
209
210impl From<InvalidUri> for ParseError {
211    fn from(err: InvalidUri) -> ParseError {
212        ParseError::Uri(err)
213    }
214}
215
216impl From<Utf8Error> for ParseError {
217    fn from(err: Utf8Error) -> ParseError {
218        ParseError::Utf8(err)
219    }
220}
221
222impl From<FromUtf8Error> for ParseError {
223    fn from(err: FromUtf8Error) -> ParseError {
224        ParseError::Utf8(err.utf8_error())
225    }
226}
227
228impl From<httparse::Error> for ParseError {
229    fn from(err: httparse::Error) -> ParseError {
230        match err {
231            httparse::Error::HeaderName
232            | httparse::Error::HeaderValue
233            | httparse::Error::NewLine
234            | httparse::Error::Token => ParseError::Header,
235            httparse::Error::Status => ParseError::Status,
236            httparse::Error::TooManyHeaders => ParseError::TooLarge,
237            httparse::Error::Version => ParseError::Version,
238        }
239    }
240}
241
242impl From<ParseError> for Error {
243    fn from(err: ParseError) -> Self {
244        Self::new_parse().with_cause(err)
245    }
246}
247
248impl From<ParseError> for Response<BoxBody> {
249    fn from(err: ParseError) -> Self {
250        Error::from(err).into()
251    }
252}
253
254/// A set of errors that can occur during payload parsing.
255#[derive(Debug, Display)]
256#[non_exhaustive]
257pub enum PayloadError {
258    /// A payload reached EOF, but is not complete.
259    #[display("payload reached EOF before completing: {:?}", _0)]
260    Incomplete(Option<io::Error>),
261
262    /// Content encoding stream corruption.
263    #[display("can not decode content-encoding")]
264    EncodingCorrupted,
265
266    /// Payload reached size limit.
267    #[display("payload reached size limit")]
268    Overflow,
269
270    /// Payload length is unknown.
271    #[display("payload length is unknown")]
272    UnknownLength,
273
274    /// HTTP/2 payload error.
275    #[cfg(feature = "http2")]
276    #[display("{}", _0)]
277    Http2Payload(::h2::Error),
278
279    /// Generic I/O error.
280    #[display("{}", _0)]
281    Io(io::Error),
282}
283
284impl std::error::Error for PayloadError {
285    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
286        match self {
287            PayloadError::Incomplete(None) => None,
288            PayloadError::Incomplete(Some(err)) => Some(err),
289            PayloadError::EncodingCorrupted => None,
290            PayloadError::Overflow => None,
291            PayloadError::UnknownLength => None,
292            #[cfg(feature = "http2")]
293            PayloadError::Http2Payload(err) => Some(err),
294            PayloadError::Io(err) => Some(err),
295        }
296    }
297}
298
299#[cfg(feature = "http2")]
300impl From<::h2::Error> for PayloadError {
301    fn from(err: ::h2::Error) -> Self {
302        PayloadError::Http2Payload(err)
303    }
304}
305
306impl From<Option<io::Error>> for PayloadError {
307    fn from(err: Option<io::Error>) -> Self {
308        PayloadError::Incomplete(err)
309    }
310}
311
312impl From<io::Error> for PayloadError {
313    fn from(err: io::Error) -> Self {
314        PayloadError::Incomplete(Some(err))
315    }
316}
317
318impl From<PayloadError> for Error {
319    fn from(err: PayloadError) -> Self {
320        Self::new_payload().with_cause(err)
321    }
322}
323
324/// A set of errors that can occur during dispatching HTTP requests.
325#[derive(Debug, Display, From)]
326#[non_exhaustive]
327pub enum DispatchError {
328    /// Service error.
329    #[display("service error")]
330    Service(Response<BoxBody>),
331
332    /// Body streaming error.
333    #[display("body error: {}", _0)]
334    Body(Box<dyn StdError>),
335
336    /// Upgrade service error.
337    #[display("upgrade error")]
338    Upgrade,
339
340    /// An `io::Error` that occurred while trying to read or write to a network stream.
341    #[display("I/O error: {}", _0)]
342    Io(io::Error),
343
344    /// Request parse error.
345    #[display("request parse error: {}", _0)]
346    Parse(ParseError),
347
348    /// HTTP/2 error.
349    #[display("{}", _0)]
350    #[cfg(feature = "http2")]
351    H2(h2::Error),
352
353    /// The first request did not complete within the specified timeout.
354    #[display("request did not complete within the specified timeout")]
355    SlowRequestTimeout,
356
357    /// Disconnect timeout. Makes sense for TLS streams.
358    #[display("connection shutdown timeout")]
359    DisconnectTimeout,
360
361    /// Handler dropped payload before reading EOF.
362    #[display("handler dropped payload before reading EOF")]
363    HandlerDroppedPayload,
364
365    /// Internal error.
366    #[display("internal error")]
367    InternalError,
368}
369
370impl StdError for DispatchError {
371    fn source(&self) -> Option<&(dyn StdError + 'static)> {
372        match self {
373            DispatchError::Service(_res) => None,
374            DispatchError::Body(err) => Some(&**err),
375            DispatchError::Io(err) => Some(err),
376            DispatchError::Parse(err) => Some(err),
377
378            #[cfg(feature = "http2")]
379            DispatchError::H2(err) => Some(err),
380
381            _ => None,
382        }
383    }
384}
385
386/// A set of error that can occur during parsing content type.
387#[derive(Debug, Display, Error)]
388#[cfg_attr(test, derive(PartialEq, Eq))]
389#[non_exhaustive]
390pub enum ContentTypeError {
391    /// Can not parse content type.
392    #[display("could not parse content type")]
393    ParseError,
394
395    /// Unknown content encoding.
396    #[display("unknown content encoding")]
397    UnknownEncoding,
398}
399
400#[cfg(test)]
401mod tests {
402    use http::Error as HttpError;
403
404    use super::*;
405
406    #[test]
407    fn test_into_response() {
408        let resp: Response<BoxBody> = ParseError::Incomplete.into();
409        assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
410
411        let err: HttpError = StatusCode::from_u16(10000).err().unwrap().into();
412        let resp: Response<BoxBody> = Error::new_http().with_cause(err).into();
413        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
414    }
415
416    #[test]
417    fn test_as_response() {
418        let orig = io::Error::new(io::ErrorKind::Other, "other");
419        let err: Error = ParseError::Io(orig).into();
420        assert_eq!(
421            format!("{}", err),
422            "error parsing HTTP message: I/O error: other"
423        );
424    }
425
426    #[test]
427    fn test_error_display() {
428        let orig = io::Error::new(io::ErrorKind::Other, "other");
429        let err = Error::new_io().with_cause(orig);
430        assert_eq!("connection error: other", err.to_string());
431    }
432
433    #[test]
434    fn test_error_http_response() {
435        let orig = io::Error::new(io::ErrorKind::Other, "other");
436        let err = Error::new_io().with_cause(orig);
437        let resp: Response<BoxBody> = err.into();
438        assert_eq!(resp.status(), StatusCode::INTERNAL_SERVER_ERROR);
439    }
440
441    #[test]
442    fn test_payload_error() {
443        let err: PayloadError = io::Error::new(io::ErrorKind::Other, "ParseError").into();
444        assert!(err.to_string().contains("ParseError"));
445
446        let err = PayloadError::Incomplete(None);
447        assert_eq!(
448            err.to_string(),
449            "payload reached EOF before completing: None"
450        );
451    }
452
453    macro_rules! from {
454        ($from:expr => $error:pat) => {
455            match ParseError::from($from) {
456                err @ $error => {
457                    assert!(err.to_string().len() >= 5);
458                }
459                err => unreachable!("{:?}", err),
460            }
461        };
462    }
463
464    macro_rules! from_and_cause {
465        ($from:expr => $error:pat) => {
466            match ParseError::from($from) {
467                e @ $error => {
468                    let desc = format!("{}", e);
469                    assert_eq!(desc, format!("I/O error: {}", $from));
470                }
471                _ => unreachable!("{:?}", $from),
472            }
473        };
474    }
475
476    #[test]
477    fn test_from() {
478        from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => ParseError::Io(..));
479        from!(httparse::Error::HeaderName => ParseError::Header);
480        from!(httparse::Error::HeaderName => ParseError::Header);
481        from!(httparse::Error::HeaderValue => ParseError::Header);
482        from!(httparse::Error::NewLine => ParseError::Header);
483        from!(httparse::Error::Status => ParseError::Status);
484        from!(httparse::Error::Token => ParseError::Header);
485        from!(httparse::Error::TooManyHeaders => ParseError::TooLarge);
486        from!(httparse::Error::Version => ParseError::Version);
487    }
488}