1use std::sync::Arc;
4
5use thiserror::Error;
6use tor_error::{Bug, ErrorKind, HasKind};
7use tor_linkspec::OwnedChanTarget;
8use tor_rtcompat::TimeoutError;
9
10use crate::SourceInfo;
11
12#[derive(Error, Debug, Clone)]
14#[non_exhaustive]
15#[allow(clippy::large_enum_variant)] pub enum Error {
17 #[error("Error while getting a circuit")]
19 CircMgr(#[from] tor_circmgr::Error),
20
21 #[error("Error fetching directory information")]
23 RequestFailed(#[from] RequestFailedError),
24
25 #[error("Internal error")]
27 Bug(#[from] Bug),
28}
29
30#[derive(Error, Debug, Clone)]
32#[allow(clippy::exhaustive_structs)] #[error("Request failed{}", FromSource(.source))]
34pub struct RequestFailedError {
35 pub source: Option<SourceInfo>,
37
38 #[source]
40 pub error: RequestError,
41}
42
43struct FromSource<'a>(&'a Option<SourceInfo>);
45
46impl std::fmt::Display for FromSource<'_> {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 if let Some(si) = self.0 {
49 write!(f, " from {}", si)
50 } else {
51 Ok(())
52 }
53 }
54}
55
56#[derive(Error, Debug, Clone)]
58#[non_exhaustive]
59pub enum RequestError {
60 #[error("directory timed out")]
62 DirTimeout,
63
64 #[error("truncated HTTP headers")]
66 TruncatedHeaders,
67
68 #[error("response too long; gave up after {0} bytes")]
70 ResponseTooLong(usize),
71
72 #[error("Couldn't decode data as UTF-8.")]
74 Utf8Encoding(#[from] std::string::FromUtf8Error),
75
76 #[error("IO error")]
78 IoError(#[source] Arc<std::io::Error>),
79
80 #[error("Protocol error while launching a stream")]
82 Proto(#[from] tor_proto::Error),
83
84 #[error("Couldn't parse HTTP headers")]
86 HttparseError(#[from] httparse::Error),
87
88 #[error("Couldn't create HTTP request")]
94 HttpError(#[source] Arc<http::Error>),
95
96 #[error("Unrecognized content encoding: {0:?}")]
98 ContentEncoding(String),
99
100 #[error("Too much clock skew with directory cache")]
105 TooMuchClockSkew,
106
107 #[error("We didn't have any objects to request")]
112 EmptyRequest,
113
114 #[error("HTTP status code {0}: {1:?}")]
116 HttpStatus(u16, String),
117}
118
119impl From<TimeoutError> for RequestError {
120 fn from(_: TimeoutError) -> Self {
121 RequestError::DirTimeout
122 }
123}
124
125impl From<std::io::Error> for RequestError {
126 fn from(err: std::io::Error) -> Self {
127 Self::IoError(Arc::new(err))
128 }
129}
130
131impl From<http::Error> for RequestError {
132 fn from(err: http::Error) -> Self {
133 Self::HttpError(Arc::new(err))
134 }
135}
136
137impl Error {
138 pub fn should_retire_circ(&self) -> bool {
141 match self {
144 Error::CircMgr(_) => true, Error::RequestFailed(RequestFailedError { error, .. }) => error.should_retire_circ(),
146 Error::Bug(_) => true,
147 }
148 }
149
150 pub fn cache_ids(&self) -> Vec<&OwnedChanTarget> {
155 match &self {
156 Error::CircMgr(e) => e.peers(),
157 Error::RequestFailed(RequestFailedError {
158 source: Some(source),
159 ..
160 }) => vec![source.cache_id()],
161 _ => Vec::new(),
162 }
163 }
164}
165
166impl RequestError {
167 pub fn should_retire_circ(&self) -> bool {
170 true
173 }
174}
175
176impl HasKind for RequestError {
177 fn kind(&self) -> ErrorKind {
178 use ErrorKind as EK;
179 use RequestError as E;
180 match self {
181 E::DirTimeout => EK::TorNetworkTimeout,
182 E::TruncatedHeaders => EK::TorProtocolViolation,
183 E::ResponseTooLong(_) => EK::TorProtocolViolation,
184 E::Utf8Encoding(_) => EK::TorProtocolViolation,
185 E::IoError(_) => EK::TorDirectoryError,
189 E::Proto(e) => e.kind(),
190 E::HttparseError(_) => EK::TorProtocolViolation,
191 E::HttpError(_) => EK::Internal,
192 E::ContentEncoding(_) => EK::TorProtocolViolation,
193 E::TooMuchClockSkew => EK::TorDirectoryError,
194 E::EmptyRequest => EK::Internal,
195 E::HttpStatus(_, _) => EK::TorDirectoryError,
196 }
197 }
198}
199
200impl HasKind for RequestFailedError {
201 fn kind(&self) -> ErrorKind {
202 self.error.kind()
203 }
204}
205
206impl HasKind for Error {
207 fn kind(&self) -> ErrorKind {
208 use Error as E;
209 match self {
210 E::CircMgr(e) => e.kind(),
211 E::RequestFailed(e) => e.kind(),
212 E::Bug(e) => e.kind(),
213 }
214 }
215}