1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
use crate::head::HeadError;
use crate::Response;
use safina_timer::{DeadlineError, DeadlineExceeded};
use std::io::ErrorKind;

#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum HttpError {
    AlreadyGotBody,
    BodyNotAvailable,
    BodyNotRead,
    BodyNotUtf8,
    BodyTooLong,
    CacheDirNotConfigured,
    Disconnected,
    DuplicateContentLengthHeader,
    DuplicateContentTypeHeader,
    DuplicateTransferEncodingHeader,
    ErrorReadingFile(ErrorKind, String),
    ErrorReadingResponseBody(ErrorKind, String),
    ErrorSavingFile(ErrorKind, String),
    HandlerDeadlineExceeded,
    HeadTooLong,
    InvalidContentLength,
    MalformedCookieHeader,
    MalformedHeaderLine,
    MalformedPath,
    MalformedRequestLine,
    MissingRequestLine,
    ResponseAlreadySent,
    ResponseNotSent,
    TimerThreadNotStarted,
    Truncated,
    UnsupportedProtocol,
    UnsupportedTransferEncoding,
    UnwritableResponse,
}
impl HttpError {
    #[must_use]
    #[allow(clippy::needless_pass_by_value)]
    pub fn error_reading_file(e: std::io::Error) -> Self {
        HttpError::ErrorReadingFile(e.kind(), e.to_string())
    }

    #[must_use]
    #[allow(clippy::needless_pass_by_value)]
    pub fn error_reading_response_body(e: std::io::Error) -> Self {
        HttpError::ErrorReadingResponseBody(e.kind(), e.to_string())
    }

    #[must_use]
    #[allow(clippy::needless_pass_by_value)]
    pub fn error_saving_file(e: std::io::Error) -> Self {
        HttpError::ErrorSavingFile(e.kind(), e.to_string())
    }

    #[must_use]
    pub fn is_server_error(&self) -> bool {
        match self {
            HttpError::AlreadyGotBody
            | HttpError::BodyNotAvailable
            | HttpError::BodyNotRead
            | HttpError::CacheDirNotConfigured
            | HttpError::DuplicateContentLengthHeader
            | HttpError::DuplicateContentTypeHeader
            | HttpError::DuplicateTransferEncodingHeader
            | HttpError::ErrorReadingFile(..)
            | HttpError::ErrorReadingResponseBody(..)
            | HttpError::ErrorSavingFile(..)
            | HttpError::HandlerDeadlineExceeded
            | HttpError::ResponseAlreadySent
            | HttpError::ResponseNotSent
            | HttpError::UnwritableResponse => true,
            HttpError::BodyNotUtf8
            | HttpError::BodyTooLong
            | HttpError::Disconnected
            | HttpError::HeadTooLong
            | HttpError::InvalidContentLength
            | HttpError::MalformedCookieHeader
            | HttpError::MalformedHeaderLine
            | HttpError::MalformedPath
            | HttpError::MalformedRequestLine
            | HttpError::MissingRequestLine
            | HttpError::TimerThreadNotStarted
            | HttpError::Truncated
            | HttpError::UnsupportedProtocol
            | HttpError::UnsupportedTransferEncoding => false,
        }
    }

    #[must_use]
    pub fn description(&self) -> String {
        match self {
            HttpError::AlreadyGotBody => "HttpError::AlreadyGotBody".to_string(),
            HttpError::BodyNotAvailable => "HttpError::BodyNotAvailable".to_string(),
            HttpError::BodyNotRead => "HttpError::BodyNotRead".to_string(),
            HttpError::BodyNotUtf8 => "HttpError::BodyNotUtf8".to_string(),
            HttpError::BodyTooLong => "HttpError::BodyTooLong".to_string(),
            HttpError::CacheDirNotConfigured => "HttpError::CacheDirNotConfigured".to_string(),
            HttpError::DuplicateContentLengthHeader => {
                "HttpError::DuplicateContentLengthHeader".to_string()
            }
            HttpError::DuplicateContentTypeHeader => {
                "HttpError::DuplicateContentTypeHeader".to_string()
            }
            HttpError::DuplicateTransferEncodingHeader => {
                "HttpError::DuplicateTransferEncodingHeader".to_string()
            }
            HttpError::Disconnected => "HttpError::Disconnected".to_string(),
            HttpError::ErrorReadingFile(kind, s) => {
                format!("HttpError::ErrorReadingFile: {kind:?}: {s}")
            }
            HttpError::ErrorReadingResponseBody(kind, s) => {
                format!("HttpError::ErrorReadingResponseBody: {kind:?}: {s}")
            }
            HttpError::ErrorSavingFile(kind, s) => {
                format!("HttpError::ErrorSavingFile: {kind:?}: {s}")
            }
            HttpError::HandlerDeadlineExceeded => "HttpError::HandlerDeadlineExceeded".to_string(),
            HttpError::HeadTooLong => "HttpError::HeadTooLong".to_string(),
            HttpError::InvalidContentLength => "HttpError::InvalidContentLength".to_string(),
            HttpError::MalformedCookieHeader => "HttpError::MalformedCookieHeader".to_string(),
            HttpError::MalformedHeaderLine => "HttpError::MalformedHeaderLine".to_string(),
            HttpError::MalformedPath => "HttpError::MalformedPath".to_string(),
            HttpError::MalformedRequestLine => "HttpError::MalformedRequestLine".to_string(),
            HttpError::MissingRequestLine => "HttpError::MissingRequestLine".to_string(),
            HttpError::ResponseAlreadySent => "HttpError::ResponseAlreadySent".to_string(),
            HttpError::ResponseNotSent => "HttpError::ResponseNotSent".to_string(),
            HttpError::TimerThreadNotStarted => "HttpError::TimerThreadNotStarted".to_string(),
            HttpError::Truncated => "HttpError::Truncated".to_string(),
            HttpError::UnsupportedProtocol => "HttpError::UnsupportedProtocol".to_string(),
            HttpError::UnsupportedTransferEncoding => {
                "HttpError::UnsupportedTransferEncoding".to_string()
            }
            HttpError::UnwritableResponse => "HttpError::UnwritableResponse".to_string(),
        }
    }
}
impl From<HeadError> for HttpError {
    fn from(e: HeadError) -> Self {
        match e {
            HeadError::Truncated => HttpError::Truncated,
            HeadError::MissingRequestLine => HttpError::MissingRequestLine,
            HeadError::MalformedRequestLine => HttpError::MalformedRequestLine,
            HeadError::MalformedPath => HttpError::MalformedPath,
            HeadError::UnsupportedProtocol => HttpError::UnsupportedProtocol,
            HeadError::MalformedHeader => HttpError::MalformedHeaderLine,
        }
    }
}
impl From<HttpError> for Response {
    fn from(e: HttpError) -> Self {
        match e {
            HttpError::BodyNotUtf8
            | HttpError::InvalidContentLength
            | HttpError::MalformedCookieHeader
            | HttpError::MalformedHeaderLine
            | HttpError::MalformedPath
            | HttpError::MalformedRequestLine
            | HttpError::MissingRequestLine
            | HttpError::Truncated
            | HttpError::UnsupportedTransferEncoding => Response::text(400, e.description()),
            HttpError::Disconnected => Response::drop_connection(),
            HttpError::BodyTooLong => Response::text(413, "Uploaded data is too big."),
            HttpError::HeadTooLong => Response::text(431, e.description()),
            HttpError::UnsupportedProtocol => Response::text(505, e.description()),
            HttpError::AlreadyGotBody
            | HttpError::BodyNotAvailable
            | HttpError::BodyNotRead
            | HttpError::CacheDirNotConfigured
            | HttpError::DuplicateContentLengthHeader
            | HttpError::DuplicateContentTypeHeader
            | HttpError::DuplicateTransferEncodingHeader
            | HttpError::ErrorReadingFile(..)
            | HttpError::ErrorReadingResponseBody(..)
            | HttpError::ErrorSavingFile(..)
            | HttpError::HandlerDeadlineExceeded
            | HttpError::ResponseAlreadySent
            | HttpError::ResponseNotSent
            | HttpError::TimerThreadNotStarted
            | HttpError::UnwritableResponse => Response::text(500, "Internal server error"),
        }
    }
}
impl From<DeadlineExceeded> for HttpError {
    fn from(_: DeadlineExceeded) -> Self {
        HttpError::HandlerDeadlineExceeded
    }
}
impl From<DeadlineError> for HttpError {
    fn from(e: DeadlineError) -> Self {
        match e {
            DeadlineError::TimerThreadNotStarted => HttpError::TimerThreadNotStarted,
            DeadlineError::DeadlineExceeded => HttpError::HandlerDeadlineExceeded,
        }
    }
}