mco_http/
error.rs

1//! Error and Result module.
2use std::error::Error as StdError;
3use std::fmt;
4use std::fmt::{Display, Formatter};
5use std::io::Error as IoError;
6use std::str::Utf8Error;
7use std::string::FromUtf8Error;
8
9use httparse;
10use url;
11
12#[cfg(feature = "openssl")]
13use openssl::ssl::error::SslError;
14
15use self::Error::{
16    Method,
17    Uri,
18    Version,
19    Header,
20    Status,
21    Io,
22    Ssl,
23    TooLarge,
24    Utf8
25};
26
27pub use url::ParseError;
28
29/// Result type often returned from methods that can have mco_http `Error`s.
30pub type Result<T> = ::std::result::Result<T, Error>;
31
32/// A set of errors that can occur parsing HTTP streams.
33#[derive(Debug)]
34pub enum Error {
35    /// An invalid `Method`, such as `GE,T`.
36    Method,
37    /// An invalid `RequestUri`, such as `exam ple.domain`.
38    Uri(url::ParseError),
39    /// An invalid `HttpVersion`, such as `HTP/1.1`
40    Version,
41    /// An invalid `Header`.
42    Header,
43    /// A message head is too large to be reasonable.
44    TooLarge,
45    /// An invalid `Status`, such as `1337 ELITE`.
46    Status,
47    /// An `io::Error` that occurred while trying to read or write to a network stream.
48    Io(IoError),
49    /// An error from a SSL library.
50    Ssl(Box<dyn StdError + Send + Sync>),
51    /// Parsing a field as string failed
52    Utf8(Utf8Error),
53
54    /// Other error
55    Other(String),
56
57    #[doc(hidden)]
58    __Nonexhaustive(Void)
59}
60
61#[doc(hidden)]
62pub struct Void(());
63
64impl fmt::Debug for Void {
65    fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
66        unreachable!()
67    }
68}
69
70impl StdError for Error {
71    fn cause(&self) -> Option<&dyn StdError> {
72        match *self {
73            Io(ref error) => Some(error),
74            Ssl(ref error) => Some(&**error),
75            Uri(ref error) => Some(error),
76            Utf8(ref error) => Some(error),
77            _ => None,
78        }
79    }
80}
81
82impl Display for Error{
83    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84        match self {
85            Method => f.write_str("Invalid Method specified"),
86            Version => f.write_str("Invalid HTTP version specified"),
87            Header => f.write_str("Invalid Header provided"),
88            TooLarge => f.write_str("Message head is too large"),
89            Status => f.write_str("Invalid Status provided"),
90            Uri(e) => write!(f, "{}", e),
91            Io(e) => write!(f, "{}", e),
92            Ssl(e) => write!(f, "{}", e),
93            Utf8(e) => write!(f, "{}", e),
94            Error::Other(e) => write!(f, "{}", e),
95            Error::__Nonexhaustive(..) =>  unreachable!(),
96        }
97    }
98}
99
100impl From<IoError> for Error {
101    fn from(err: IoError) -> Error {
102        Io(err)
103    }
104}
105
106impl From<url::ParseError> for Error {
107    fn from(err: url::ParseError) -> Error {
108        Uri(err)
109    }
110}
111
112#[cfg(feature = "openssl")]
113impl From<SslError> for Error {
114    fn from(err: SslError) -> Error {
115        match err {
116            SslError::StreamError(err) => Io(err),
117            err => Ssl(Box::new(err)),
118        }
119    }
120}
121
122impl From<Utf8Error> for Error {
123    fn from(err: Utf8Error) -> Error {
124        Utf8(err)
125    }
126}
127
128impl From<FromUtf8Error> for Error {
129    fn from(err: FromUtf8Error) -> Error {
130        Utf8(err.utf8_error())
131    }
132}
133
134impl From<httparse::Error> for Error {
135    fn from(err: httparse::Error) -> Error {
136        match err {
137            httparse::Error::HeaderName => Header,
138            httparse::Error::HeaderValue => Header,
139            httparse::Error::NewLine => Header,
140            httparse::Error::Status => Status,
141            httparse::Error::Token => Header,
142            httparse::Error::TooManyHeaders => TooLarge,
143            httparse::Error::Version => Version,
144        }
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use std::error::Error as StdError;
151    use std::io;
152    use httparse;
153    use url;
154    use super::Error;
155    use super::Error::*;
156
157    #[test]
158    fn test_cause() {
159        let orig = io::Error::new(io::ErrorKind::Other, "other");
160        let desc = orig.description().to_owned();
161        let e = Io(orig);
162        assert_eq!(e.cause().unwrap().description(), desc);
163    }
164
165    macro_rules! from {
166        ($from:expr => $error:pat) => {
167            match Error::from($from) {
168                e @ $error => {
169                    assert!(e.description().len() > 5);
170                } ,
171                _ => panic!("{:?}", $from)
172            }
173        }
174    }
175
176    macro_rules! from_and_cause {
177        ($from:expr => $error:pat) => {
178            match Error::from($from) {
179                e @ $error => {
180                    let desc = e.cause().unwrap().to_string();
181                    assert_eq!(desc, $from.to_string().to_owned());
182                    assert_eq!(desc, e.to_string());
183                },
184                _ => panic!("{:?}", $from)
185            }
186        }
187    }
188
189    #[test]
190    fn test_from() {
191
192        from_and_cause!(io::Error::new(io::ErrorKind::Other, "other") => Io(..));
193        from_and_cause!(url::ParseError::EmptyHost => Uri(..));
194
195        from!(httparse::Error::HeaderName => Header);
196        from!(httparse::Error::HeaderName => Header);
197        from!(httparse::Error::HeaderValue => Header);
198        from!(httparse::Error::NewLine => Header);
199        from!(httparse::Error::Status => Status);
200        from!(httparse::Error::Token => Header);
201        from!(httparse::Error::TooManyHeaders => TooLarge);
202        from!(httparse::Error::Version => Version);
203    }
204
205    #[cfg(feature = "openssl")]
206    #[test]
207    fn test_from_ssl() {
208        use openssl::ssl::error::SslError;
209
210        from!(SslError::StreamError(
211            io::Error::new(io::ErrorKind::Other, "ssl negotiation")) => Io(..));
212        from_and_cause!(SslError::SslSessionClosed => Ssl(..));
213    }
214}