Extract IO errors from h2 for streaming retries of Connection Reset (#15675)
Our streaming retries were missing connection reset errors as h2 was shadowing IO errors (https://github.com/hyperium/h2/issues/862). **Test plan** In one terminal: ``` cargo python uninstall 3.12 && cargo run python install 3.12 -vv ``` In another: ``` sudo tcpkill -i wlp2s0 port 443 ``` Output: ``` error: Failed to install cpython-3.12.11-linux-x86_64-gnu Caused by: Request failed after 3 retries Caused by: Failed to download https://github.com/astral-sh/python-build-standalone/releases/download/20250902/cpython-3.12.11%2B20250902-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz Caused by: error sending request for url (https://github.com/astral-sh/python-build-standalone/releases/download/20250902/cpython-3.12.11%2B20250902-x86_64-unknown-linux-gnu-install_only_stripped.tar.gz) Caused by: client error (SendRequest) Caused by: connection error Caused by: connection reset ``` I don't know how to test that from inside Rust. Fix #14171 (again, hopefully)
This commit is contained in:
Generated
+1
@@ -5380,6 +5380,7 @@ dependencies = [
|
||||
"bytecheck",
|
||||
"fs-err",
|
||||
"futures",
|
||||
"h2",
|
||||
"html-escape",
|
||||
"http",
|
||||
"http-body-util",
|
||||
|
||||
@@ -38,6 +38,7 @@ async_zip = { workspace = true }
|
||||
bytecheck = { workspace = true }
|
||||
fs-err = { workspace = true, features = ["tokio"] }
|
||||
futures = { workspace = true }
|
||||
h2 = { workspace = true }
|
||||
html-escape = { workspace = true }
|
||||
http = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
|
||||
@@ -983,7 +983,7 @@ impl RetryableStrategy for UvRetryableStrategy {
|
||||
/// Whether the error looks like a network error that should be retried.
|
||||
///
|
||||
/// There are two cases that the default retry strategy is missing:
|
||||
/// * Inside the reqwest middleware error is an `io::Error` such as a broken pipe
|
||||
/// * Inside the reqwest or reqwest-middleware error is an `io::Error` such as a broken pipe
|
||||
/// * When streaming a response, a reqwest error may be hidden several layers behind errors
|
||||
/// of different crates processing the stream, including `io::Error` layers.
|
||||
pub fn is_transient_network_error(err: &(dyn Error + 'static)) -> bool {
|
||||
@@ -1027,7 +1027,12 @@ pub fn is_transient_network_error(err: &(dyn Error + 'static)) -> bool {
|
||||
}
|
||||
|
||||
trace!("Cannot retry nested reqwest error");
|
||||
} else if let Some(io_err) = source.downcast_ref::<io::Error>() {
|
||||
} else if let Some(io_err) = source.downcast_ref::<io::Error>().or_else(|| {
|
||||
// h2 may hide an IO error inside.
|
||||
source
|
||||
.downcast_ref::<h2::Error>()
|
||||
.and_then(|err| err.get_io())
|
||||
}) {
|
||||
has_known_error = true;
|
||||
let retryable_io_err_kinds = [
|
||||
// https://github.com/astral-sh/uv/issues/12054
|
||||
|
||||
Reference in New Issue
Block a user