1use 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)] pub(crate) fn new_io() -> Self {
55 Self::new(Kind::Io)
56 }
57
58 #[allow(unused)] pub(crate) fn new_encoder() -> Self {
60 Self::new(Kind::Encoder)
61 }
62
63 #[allow(unused)] 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 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#[derive(Debug, Display, Error)]
160#[non_exhaustive]
161pub enum ParseError {
162 #[display("invalid method specified")]
164 Method,
165
166 #[display("URI error: {}", _0)]
168 Uri(InvalidUri),
169
170 #[display("invalid HTTP version specified")]
172 Version,
173
174 #[display("invalid Header provided")]
176 Header,
177
178 #[display("message head is too large")]
180 TooLarge,
181
182 #[display("message is incomplete")]
184 Incomplete,
185
186 #[display("invalid status provided")]
188 Status,
189
190 #[allow(dead_code)]
192 #[display("timeout")]
193 Timeout,
194
195 #[display("I/O error: {}", _0)]
197 Io(io::Error),
198
199 #[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#[derive(Debug, Display)]
256#[non_exhaustive]
257pub enum PayloadError {
258 #[display("payload reached EOF before completing: {:?}", _0)]
260 Incomplete(Option<io::Error>),
261
262 #[display("can not decode content-encoding")]
264 EncodingCorrupted,
265
266 #[display("payload reached size limit")]
268 Overflow,
269
270 #[display("payload length is unknown")]
272 UnknownLength,
273
274 #[cfg(feature = "http2")]
276 #[display("{}", _0)]
277 Http2Payload(::h2::Error),
278
279 #[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#[derive(Debug, Display, From)]
326#[non_exhaustive]
327pub enum DispatchError {
328 #[display("service error")]
330 Service(Response<BoxBody>),
331
332 #[display("body error: {}", _0)]
334 Body(Box<dyn StdError>),
335
336 #[display("upgrade error")]
338 Upgrade,
339
340 #[display("I/O error: {}", _0)]
342 Io(io::Error),
343
344 #[display("request parse error: {}", _0)]
346 Parse(ParseError),
347
348 #[display("{}", _0)]
350 #[cfg(feature = "http2")]
351 H2(h2::Error),
352
353 #[display("request did not complete within the specified timeout")]
355 SlowRequestTimeout,
356
357 #[display("connection shutdown timeout")]
359 DisconnectTimeout,
360
361 #[display("handler dropped payload before reading EOF")]
363 HandlerDroppedPayload,
364
365 #[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#[derive(Debug, Display, Error)]
388#[cfg_attr(test, derive(PartialEq, Eq))]
389#[non_exhaustive]
390pub enum ContentTypeError {
391 #[display("could not parse content type")]
393 ParseError,
394
395 #[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}