1use sova_core::extend::ErrorResponse;
4use sova_core::{Error, IntoResponse, Response};
5use std::fmt;
6
7#[derive(Debug)]
9pub enum HttpError {
10 Timeout,
11 Connect(String),
12 ResponseTooLarge,
13 Ssrf(String),
14 CircuitOpen(String),
15 Status(u16),
16 Other(String),
17}
18
19impl fmt::Display for HttpError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::Timeout => write!(f, "upstream timeout"),
23 Self::Connect(m) => write!(f, "upstream connect: {m}"),
24 Self::ResponseTooLarge => write!(f, "upstream response too large"),
25 Self::Ssrf(m) => write!(f, "ssrf blocked: {m}"),
26 Self::CircuitOpen(host) => write!(f, "circuit open for {host}"),
27 Self::Status(c) => write!(f, "upstream status {c}"),
28 Self::Other(m) => write!(f, "{m}"),
29 }
30 }
31}
32
33impl std::error::Error for HttpError {}
34
35impl HttpError {
36 pub fn status_code(&self) -> u16 {
37 match self {
38 Self::Timeout => 504,
39 Self::Connect(_)
40 | Self::ResponseTooLarge
41 | Self::Ssrf(_)
42 | Self::CircuitOpen(_)
43 | Self::Status(_)
44 | Self::Other(_) => 502,
45 }
46 }
47}
48
49impl IntoResponse for HttpError {
50 fn into_response(self) -> Response {
51 let status = self.status_code();
52 let msg = match status {
53 504 => "Gateway Timeout",
54 _ => "Bad Gateway",
55 };
56 Response::text(msg).status(status)
57 }
58}
59
60impl From<HttpError> for Error {
61 fn from(err: HttpError) -> Self {
62 Error::Response(Box::new(err.into_response()))
63 }
64}
65
66impl ErrorResponse for HttpError {}
67
68impl From<reqwest::Error> for HttpError {
69 fn from(err: reqwest::Error) -> Self {
70 if err.is_timeout() {
71 return Self::Timeout;
72 }
73 if err.is_connect() {
74 return Self::Connect(err.to_string());
75 }
76 Self::Other(err.to_string())
77 }
78}