|
| 1 | +use std; |
| 2 | +use std::error::Error; |
| 3 | + |
| 4 | +use error_chain::ChainedError; |
| 5 | + |
1 | 6 | use util::Config;
|
2 | 7 | use util::errors::{CargoError, CargoErrorKind, CargoResult};
|
3 | 8 |
|
4 | 9 | use git2;
|
| 10 | +fn maybe_spurious<E, EKind>(err: &E) -> bool |
| 11 | + where E: ChainedError<ErrorKind=EKind> + 'static { |
| 12 | + //Error inspection in non-verbose mode requires inspecting the |
| 13 | + //error kind to avoid printing Internal errors. The downcasting |
| 14 | + //machinery requires &(Error + 'static), but the iterator (and |
| 15 | + //underlying `cause`) return &Error. Because the borrows are |
| 16 | + //constrained to this handling method, and because the original |
| 17 | + //error object is constrained to be 'static, we're casting away |
| 18 | + //the borrow's actual lifetime for purposes of downcasting and |
| 19 | + //inspecting the error chain |
| 20 | + unsafe fn extend_lifetime(r: &Error) -> &(Error + 'static) { |
| 21 | + std::mem::transmute::<&Error, &Error>(r) |
| 22 | + } |
5 | 23 |
|
6 |
| -fn maybe_spurious(err: &CargoError) -> bool { |
7 |
| - match err.kind() { |
8 |
| - &CargoErrorKind::Git(ref git_err) => { |
9 |
| - match git_err.class() { |
10 |
| - git2::ErrorClass::Net | |
11 |
| - git2::ErrorClass::Os => true, |
12 |
| - _ => false |
| 24 | + for e in err.iter() { |
| 25 | + let e = unsafe { extend_lifetime(e) }; |
| 26 | + if let Some(cargo_err) = e.downcast_ref::<CargoError>() { |
| 27 | + match cargo_err.kind() { |
| 28 | + &CargoErrorKind::Git(ref git_err) => { |
| 29 | + match git_err.class() { |
| 30 | + git2::ErrorClass::Net | |
| 31 | + git2::ErrorClass::Os => return true, |
| 32 | + _ => () |
| 33 | + } |
13 | 34 | }
|
| 35 | + &CargoErrorKind::Curl(ref curl_err) |
| 36 | + if curl_err.is_couldnt_connect() || |
| 37 | + curl_err.is_couldnt_resolve_proxy() || |
| 38 | + curl_err.is_couldnt_resolve_host() || |
| 39 | + curl_err.is_operation_timedout() || |
| 40 | + curl_err.is_recv_error() => { |
| 41 | + return true |
| 42 | + } |
| 43 | + &CargoErrorKind::HttpNot200(code, ref _url) if 500 <= code && code < 600 => { |
| 44 | + return true |
| 45 | + } |
| 46 | + _ => () |
14 | 47 | }
|
15 |
| - &CargoErrorKind::Curl(ref curl_err) => { |
16 |
| - curl_err.is_couldnt_connect() || |
17 |
| - curl_err.is_couldnt_resolve_proxy() || |
18 |
| - curl_err.is_couldnt_resolve_host() || |
19 |
| - curl_err.is_operation_timedout() || |
20 |
| - curl_err.is_recv_error() |
21 |
| - } |
22 |
| - &CargoErrorKind::HttpNot200(code, ref _url) => { |
23 |
| - 500 <= code && code < 600 |
24 |
| - } |
25 |
| - _ => false |
| 48 | + } |
26 | 49 | }
|
| 50 | + false |
27 | 51 | }
|
28 | 52 |
|
29 | 53 | /// Wrapper method for network call retry logic.
|
@@ -67,3 +91,17 @@ fn with_retry_repeats_the_call_then_works() {
|
67 | 91 | let result = with_retry(&config, || results.pop().unwrap());
|
68 | 92 | assert_eq!(result.unwrap(), ())
|
69 | 93 | }
|
| 94 | + |
| 95 | +#[test] |
| 96 | +fn with_retry_finds_nested_spurious_errors() { |
| 97 | + //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry |
| 98 | + //String error messages are not considered spurious |
| 99 | + let error1 : CargoError = CargoErrorKind::HttpNot200(501, "Uri".to_string()).into(); |
| 100 | + let error1 = CargoError::with_chain(error1, "A non-spurious wrapping err"); |
| 101 | + let error2 = CargoError::from_kind(CargoErrorKind::HttpNot200(502, "Uri".to_string())); |
| 102 | + let error2 = CargoError::with_chain(error2, "A second chained error"); |
| 103 | + let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)]; |
| 104 | + let config = Config::default().unwrap(); |
| 105 | + let result = with_retry(&config, || results.pop().unwrap()); |
| 106 | + assert_eq!(result.unwrap(), ()) |
| 107 | +} |
0 commit comments