/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ureq-3.5.0/src/error.rs
Line | Count | Source |
1 | | use std::{fmt, io}; |
2 | | |
3 | | use crate::Timeout; |
4 | | use crate::http; |
5 | | |
6 | | /// Errors from ureq. |
7 | | #[derive(Debug)] |
8 | | #[non_exhaustive] |
9 | | pub enum Error { |
10 | | /// When [`http_status_as_error()`](crate::config::ConfigBuilder::http_status_as_error) is true, |
11 | | /// 4xx and 5xx response status codes are translated to this error. |
12 | | /// |
13 | | /// This is the default behavior. |
14 | | StatusCode(u16), |
15 | | |
16 | | /// Errors arising from the http-crate. |
17 | | /// |
18 | | /// These errors happen for things like invalid characters in header names. |
19 | | Http(http::Error), |
20 | | |
21 | | /// Error if the URI is missing scheme or host. |
22 | | BadUri(String), |
23 | | |
24 | | /// An HTTP/1.1 protocol error. |
25 | | /// |
26 | | /// This can happen if the remote server ends incorrect HTTP data like |
27 | | /// missing version or invalid chunked transfer. |
28 | | Protocol(ureq_proto::Error), |
29 | | |
30 | | /// Error in io such as the TCP socket. |
31 | | Io(io::Error), |
32 | | |
33 | | /// Error raised if the request hits any configured timeout. |
34 | | /// |
35 | | /// Timeouts that return this error are disabled by default. The default one-second |
36 | | /// [`100-continue wait`](crate::config::ConfigBuilder::timeout_await_100) |
37 | | /// instead proceeds with sending the request body when it expires. |
38 | | Timeout(Timeout), |
39 | | |
40 | | /// Error when resolving a hostname fails. |
41 | | HostNotFound, |
42 | | |
43 | | /// A redirect failed. |
44 | | /// |
45 | | /// This happens when ureq encounters a redirect when sending a request body |
46 | | /// such as a POST request, and receives a 307/308 response. ureq refuses to |
47 | | /// redirect the POST body and instead raises this error. |
48 | | RedirectFailed, |
49 | | |
50 | | /// Error when creating proxy settings. |
51 | | InvalidProxyUrl, |
52 | | |
53 | | /// A connection failed. |
54 | | /// |
55 | | /// This is a fallback error when there is no other explanation as to |
56 | | /// why a connector chain didn't produce a connection. The idea is that the connector |
57 | | /// chain would return some other [`Error`] rather than rely om this value. |
58 | | /// |
59 | | /// Typically bespoke connector chains should, as far as possible, map their underlying |
60 | | /// errors to [`Error::Io`] and use the [`io::ErrorKind`] to provide a reason. |
61 | | /// |
62 | | /// A bespoke chain is allowed to map to this value, but that provides very little |
63 | | /// information to the user as to why the connection failed. One way to mitigate that |
64 | | /// would be to rely on the `log` crate to provide additional information. |
65 | | ConnectionFailed, |
66 | | |
67 | | /// A send body (Such as `&str`) is larger than the `content-length` header. |
68 | | BodyExceedsLimit(u64), |
69 | | |
70 | | /// Too many redirects. |
71 | | /// |
72 | | /// The error can be turned off by setting |
73 | | /// [`max_redirects_will_error()`](crate::config::ConfigBuilder::max_redirects_will_error) |
74 | | /// to false. When turned off, the last response will be returned instead of causing |
75 | | /// an error, even if it is a redirect. |
76 | | /// |
77 | | /// The number of redirects is limited to 10 by default. |
78 | | TooManyRedirects, |
79 | | |
80 | | /// Some error with TLS. |
81 | | // #[cfg(feature = "_tls")] This is deliberately not _tls, because we want to let |
82 | | // bespoke TLS transports use this error code. |
83 | | Tls(&'static str), |
84 | | |
85 | | /// Error in reading PEM certificates/private keys. |
86 | | /// |
87 | | /// *Note:* The wrapped error struct is not considered part of ureq API. |
88 | | /// Breaking changes in that struct will not be reflected in ureq |
89 | | /// major versions. |
90 | | #[cfg(feature = "_tls")] |
91 | | Pem(rustls_pki_types::pem::Error), |
92 | | |
93 | | /// An error originating in Rustls. |
94 | | /// |
95 | | /// *Note:* The wrapped error struct is not considered part of ureq API. |
96 | | /// Breaking changes in that struct will not be reflected in ureq |
97 | | /// major versions. |
98 | | #[cfg(feature = "_rustls")] |
99 | | Rustls(rustls::Error), |
100 | | |
101 | | /// An error originating in Native-TLS. |
102 | | /// |
103 | | /// *Note:* The wrapped error struct is not considered part of ureq API. |
104 | | /// Breaking changes in that struct will not be reflected in ureq |
105 | | /// major versions. |
106 | | #[cfg(feature = "native-tls-no-default")] |
107 | | NativeTls(native_tls::Error), |
108 | | |
109 | | /// An error providing DER encoded certificates or private keys to Native-TLS. |
110 | | /// |
111 | | /// *Note:* The wrapped error struct is not considered part of ureq API. |
112 | | /// Breaking changes in that struct will not be reflected in ureq |
113 | | /// major versions. |
114 | | #[cfg(feature = "native-tls-no-default")] |
115 | | Der(der::Error), |
116 | | |
117 | | /// An error with the cookies. |
118 | | /// |
119 | | /// *Note:* The wrapped error struct is not considered part of ureq API. |
120 | | /// Breaking changes in that struct will not be reflected in ureq |
121 | | /// major versions. |
122 | | #[cfg(feature = "cookies")] |
123 | | Cookie(cookie_store::CookieError), |
124 | | |
125 | | /// An error parsing a cookie value. |
126 | | #[cfg(feature = "cookies")] |
127 | | CookieValue(&'static str), |
128 | | |
129 | | /// An error in the cookie store. |
130 | | /// |
131 | | /// *Note:* The wrapped error struct is not considered part of ureq API. |
132 | | /// Breaking changes in that struct will not be reflected in ureq |
133 | | /// major versions. |
134 | | #[cfg(feature = "cookies")] |
135 | | CookieJar(cookie_store::Error), |
136 | | |
137 | | /// An unrecognised character set. |
138 | | #[cfg(feature = "charset")] |
139 | | UnknownCharset(String), |
140 | | |
141 | | /// The setting [`https_only`](crate::config::ConfigBuilder::https_only) is true and |
142 | | /// the URI is not https. |
143 | | RequireHttpsOnly(String), |
144 | | |
145 | | /// The response header, from status up until body, is too big. |
146 | | LargeResponseHeader(usize, usize), |
147 | | |
148 | | /// Body decompression failed (gzip or brotli). |
149 | | #[cfg(any(feature = "gzip", feature = "brotli"))] |
150 | | Decompress(&'static str, io::Error), |
151 | | |
152 | | /// Serde JSON error. |
153 | | #[cfg(feature = "json")] |
154 | | Json(serde_json::Error), |
155 | | |
156 | | /// Invalid MIME type in multipart form. |
157 | | /// |
158 | | /// *Note:* The wrapped error struct is not considered part of ureq API. |
159 | | /// Breaking changes in that struct will not be reflected in ureq |
160 | | /// major versions. |
161 | | #[cfg(feature = "multipart")] |
162 | | InvalidMimeType(mime_guess::mime::FromStrError), |
163 | | |
164 | | /// Attempt to connect to a CONNECT proxy failed. |
165 | | ConnectProxyFailed(String), |
166 | | |
167 | | /// The protocol requires TLS (https), but the connector did not |
168 | | /// create a TLS secured transport. |
169 | | /// |
170 | | /// This typically indicates a fault in bespoke `Connector` chains. |
171 | | TlsRequired, |
172 | | |
173 | | /// Some other error occured. |
174 | | /// |
175 | | /// This is an escape hatch for bespoke connector chains having errors that don't naturally |
176 | | /// map to any other error. For connector chains we recommend: |
177 | | /// |
178 | | /// 1. Map to [`Error::Io`] as far as possible. |
179 | | /// 2. Map to other [`Error`] where reasonable. |
180 | | /// 3. Fall back on [`Error::Other`]. |
181 | | /// 4. As a last resort [`Error::ConnectionFailed`]. |
182 | | /// |
183 | | /// ureq does not produce this error using the default connectors. |
184 | | Other(Box<dyn std::error::Error + Send + Sync>), |
185 | | |
186 | | /// ureq-proto made no progress and there is no more input to read. |
187 | | /// |
188 | | /// We should never see this value. |
189 | | #[doc(hidden)] |
190 | | BodyStalled, |
191 | | } |
192 | | |
193 | | impl std::error::Error for Error {} |
194 | | |
195 | | impl Error { |
196 | | /// Convert the error into a [`std::io::Error`]. |
197 | | /// |
198 | | /// If the error is [`Error::Io`], we unpack the error. In othe cases we make |
199 | | /// an `std::io::ErrorKind::Other`. |
200 | 0 | pub fn into_io(self) -> io::Error { |
201 | 0 | if let Self::Io(e) = self { |
202 | 0 | e |
203 | | } else { |
204 | 0 | io::Error::other(self) |
205 | | } |
206 | 0 | } |
207 | | |
208 | 0 | pub(crate) fn disconnected(reason: &'static str) -> Error { |
209 | 0 | trace!("UnexpectedEof reason: {}", reason); |
210 | 0 | io::Error::new(io::ErrorKind::UnexpectedEof, "Peer disconnected").into() |
211 | 0 | } |
212 | | } |
213 | | |
214 | 0 | pub(crate) fn is_wrapped_ureq_error(e: &io::Error) -> bool { |
215 | 0 | e.get_ref().map(|x| x.is::<Error>()).unwrap_or(false) |
216 | 0 | } |
217 | | |
218 | | impl From<io::Error> for Error { |
219 | 0 | fn from(e: io::Error) -> Self { |
220 | 0 | if is_wrapped_ureq_error(&e) { |
221 | | // unwraps are ok, see above. |
222 | 0 | let boxed = e.into_inner().unwrap(); |
223 | 0 | let ureq = boxed.downcast::<Error>().unwrap(); |
224 | 0 | *ureq |
225 | | } else { |
226 | 0 | Error::Io(e) |
227 | | } |
228 | 0 | } |
229 | | } |
230 | | |
231 | | impl fmt::Display for Error { |
232 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
233 | 0 | match self { |
234 | 0 | Error::StatusCode(v) => write!(f, "http status: {}", v), |
235 | 0 | Error::Http(v) => write!(f, "http: {}", v), |
236 | 0 | Error::BadUri(v) => write!(f, "bad uri: {}", v), |
237 | 0 | Error::Protocol(v) => write!(f, "protocol: {}", v), |
238 | 0 | Error::Io(v) => write!(f, "io: {}", v), |
239 | 0 | Error::Timeout(v) => write!(f, "timeout: {}", v), |
240 | 0 | Error::HostNotFound => write!(f, "host not found"), |
241 | 0 | Error::RedirectFailed => write!(f, "redirect failed"), |
242 | 0 | Error::InvalidProxyUrl => write!(f, "invalid proxy url"), |
243 | 0 | Error::ConnectionFailed => write!(f, "connection failed"), |
244 | 0 | Error::BodyExceedsLimit(v) => { |
245 | 0 | write!(f, "the response body is larger than request limit: {}", v) |
246 | | } |
247 | 0 | Error::TooManyRedirects => write!(f, "too many redirects"), |
248 | 0 | Error::Tls(v) => write!(f, "{}", v), |
249 | | #[cfg(feature = "_tls")] |
250 | 0 | Error::Pem(v) => write!(f, "PEM: {:?}", v), |
251 | | #[cfg(feature = "_rustls")] |
252 | 0 | Error::Rustls(v) => write!(f, "rustls: {}", v), |
253 | | #[cfg(feature = "native-tls-no-default")] |
254 | | Error::NativeTls(v) => write!(f, "native-tls: {}", v), |
255 | | #[cfg(feature = "native-tls-no-default")] |
256 | | Error::Der(v) => write!(f, "der: {}", v), |
257 | | #[cfg(feature = "cookies")] |
258 | | Error::Cookie(v) => write!(f, "cookie: {}", v), |
259 | | #[cfg(feature = "cookies")] |
260 | | Error::CookieValue(v) => write!(f, "{}", v), |
261 | | #[cfg(feature = "cookies")] |
262 | | Error::CookieJar(v) => write!(f, "cookie: {}", v), |
263 | | #[cfg(feature = "charset")] |
264 | | Error::UnknownCharset(v) => write!(f, "unknown character set: {}", v), |
265 | 0 | Error::RequireHttpsOnly(v) => write!(f, "configured for https only: {}", v), |
266 | 0 | Error::LargeResponseHeader(x, y) => { |
267 | 0 | write!(f, "response header is too big: {} > {}", x, y) |
268 | | } |
269 | | #[cfg(any(feature = "gzip", feature = "brotli"))] |
270 | | Error::Decompress(x, y) => write!(f, "{} decompression failed: {}", x, y), |
271 | | #[cfg(feature = "json")] |
272 | | Error::Json(v) => write!(f, "json: {}", v), |
273 | | #[cfg(feature = "multipart")] |
274 | | Error::InvalidMimeType(v) => write!(f, "invalid MIME type: {}", v), |
275 | 0 | Error::ConnectProxyFailed(v) => write!(f, "CONNECT proxy failed: {}", v), |
276 | 0 | Error::TlsRequired => write!(f, "TLS required, but transport is unsecured"), |
277 | 0 | Error::Other(v) => write!(f, "other: {}", v), |
278 | 0 | Error::BodyStalled => write!(f, "body data reading stalled"), |
279 | | } |
280 | 0 | } |
281 | | } |
282 | | |
283 | | impl From<http::Error> for Error { |
284 | 0 | fn from(value: http::Error) -> Self { |
285 | 0 | Self::Http(value) |
286 | 0 | } |
287 | | } |
288 | | |
289 | | impl From<ureq_proto::Error> for Error { |
290 | 0 | fn from(value: ureq_proto::Error) -> Self { |
291 | 0 | Self::Protocol(value) |
292 | 0 | } |
293 | | } |
294 | | |
295 | | #[cfg(feature = "_rustls")] |
296 | | impl From<rustls::Error> for Error { |
297 | 0 | fn from(value: rustls::Error) -> Self { |
298 | 0 | Self::Rustls(value) |
299 | 0 | } |
300 | | } |
301 | | |
302 | | #[cfg(feature = "native-tls-no-default")] |
303 | | impl From<native_tls::Error> for Error { |
304 | | fn from(value: native_tls::Error) -> Self { |
305 | | Self::NativeTls(value) |
306 | | } |
307 | | } |
308 | | |
309 | | #[cfg(feature = "native-tls-no-default")] |
310 | | impl From<der::Error> for Error { |
311 | | fn from(value: der::Error) -> Self { |
312 | | Self::Der(value) |
313 | | } |
314 | | } |
315 | | |
316 | | #[cfg(feature = "cookies")] |
317 | | impl From<cookie_store::CookieError> for Error { |
318 | | fn from(value: cookie_store::CookieError) -> Self { |
319 | | Self::Cookie(value) |
320 | | } |
321 | | } |
322 | | |
323 | | #[cfg(feature = "cookies")] |
324 | | impl From<cookie_store::Error> for Error { |
325 | | fn from(value: cookie_store::Error) -> Self { |
326 | | Self::CookieJar(value) |
327 | | } |
328 | | } |
329 | | |
330 | | #[cfg(feature = "json")] |
331 | | impl From<serde_json::Error> for Error { |
332 | | fn from(value: serde_json::Error) -> Self { |
333 | | Self::Json(value) |
334 | | } |
335 | | } |
336 | | |
337 | | #[cfg(test)] |
338 | | mod test { |
339 | | |
340 | | use super::*; |
341 | | |
342 | | #[test] |
343 | | #[cfg(feature = "_test")] |
344 | | fn status_code_error_redirect() { |
345 | | use crate::test::init_test_log; |
346 | | use crate::transport::set_handler; |
347 | | init_test_log(); |
348 | | set_handler( |
349 | | "/redirect_a", |
350 | | 302, |
351 | | &[("Location", "http://example.edu/redirect_b")], |
352 | | &[], |
353 | | ); |
354 | | set_handler( |
355 | | "/redirect_b", |
356 | | 302, |
357 | | &[("Location", "http://example.com/status/500")], |
358 | | &[], |
359 | | ); |
360 | | set_handler("/status/500", 500, &[], &[]); |
361 | | let err = crate::get("http://example.org/redirect_a") |
362 | | .call() |
363 | | .unwrap_err(); |
364 | | assert!(matches!(err, Error::StatusCode(500))); |
365 | | } |
366 | | |
367 | | #[test] |
368 | | fn ensure_error_size() { |
369 | | // This is platform dependent, so we can't be too strict or precise. |
370 | | let size = std::mem::size_of::<Error>(); |
371 | | assert!(size < 100); // 40 on Macbook M1 |
372 | | } |
373 | | } |