Skip to content

Commit 9a7177c

Browse files
authored
Merge pull request #2121 from kinnison/kinnison/retry-downloads
Kinnison/retry downloads
2 parents a3d6797 + e1a5fca commit 9a7177c

File tree

5 files changed

+41
-23
lines changed

5 files changed

+41
-23
lines changed

CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,13 @@ than 1, it is clamped to 1 at minimum.
140140
This is not meant for use by users, but can be suggested in diagnosing an issue
141141
should one arise with the backtrack limits.
142142

143+
### `RUSTUP_MAX_RETRIES`
144+
145+
When downloading a file, rustup will retry the download a number of times. The
146+
default is 3 times, but if this variable is set to a valid usize then it is the
147+
max retry count. A value of `0` means no retries, thus the default of `3` will
148+
mean a download is tried a total of four times before failing out.
149+
143150
### `RUSTUP_BACKTRACE`
144151

145152
By default while running tests, we unset some environment variables that will

src/dist/manifestation.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ use crate::dist::prefix::InstallPrefix;
1212
use crate::dist::temp;
1313
use crate::errors::*;
1414
use crate::utils::utils;
15+
use retry::delay::NoDelay;
16+
use retry::{retry, OperationResult};
1517
use std::path::Path;
1618

1719
pub const DIST_MANIFEST: &str = "multirust-channel-manifest.toml";
@@ -151,6 +153,13 @@ impl Manifestation {
151153
let mut things_to_install: Vec<(Component, Format, File)> = Vec::new();
152154
let mut things_downloaded: Vec<String> = Vec::new();
153155
let components = update.components_urls_and_hashes(new_manifest)?;
156+
157+
const DEFAULT_MAX_RETRIES: usize = 3;
158+
let max_retries: usize = std::env::var("RUSTUP_MAX_RETRIES")
159+
.ok()
160+
.and_then(|s| s.parse().ok())
161+
.unwrap_or(DEFAULT_MAX_RETRIES);
162+
154163
for (component, format, url, hash) in components {
155164
notify_handler(Notification::DownloadingComponent(
156165
&component.short_name(new_manifest),
@@ -165,9 +174,21 @@ impl Manifestation {
165174

166175
let url_url = utils::parse_url(&url)?;
167176

168-
let downloaded_file = download_cfg
169-
.download(&url_url, &hash)
170-
.chain_err(|| ErrorKind::ComponentDownloadFailed(component.name(new_manifest)))?;
177+
let downloaded_file = retry(NoDelay.take(max_retries), || {
178+
match download_cfg.download(&url_url, &hash) {
179+
Ok(f) => OperationResult::Ok(f),
180+
Err(e) => match e.kind() {
181+
// If there was a broken partial file, try again
182+
ErrorKind::DownloadingFile { .. } | ErrorKind::BrokenPartialFile => {
183+
notify_handler(Notification::RetryingDownload(&url));
184+
OperationResult::Retry(e)
185+
}
186+
187+
_ => OperationResult::Err(e),
188+
},
189+
}
190+
})
191+
.chain_err(|| ErrorKind::ComponentDownloadFailed(component.name(new_manifest)))?;
171192

172193
things_downloaded.push(hash);
173194

src/dist/notifications.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ pub enum Notification<'a> {
3737
ComponentUnavailable(&'a str, Option<&'a TargetTriple>),
3838
StrayHash(&'a Path),
3939
SignatureInvalid(&'a str),
40+
RetryingDownload(&'a str),
4041
}
4142

4243
impl<'a> From<crate::utils::Notification<'a>> for Notification<'a> {
@@ -72,6 +73,7 @@ impl<'a> Notification<'a> {
7273
| RollingBack
7374
| DownloadingManifest(_)
7475
| SkippingNightlyMissingComponent(_)
76+
| RetryingDownload(_)
7577
| DownloadedManifest(_, _) => NotificationLevel::Info,
7678
CantReadUpdateHash(_)
7779
| ExtensionNotInstalled(_)
@@ -181,6 +183,7 @@ impl<'a> Display for Notification<'a> {
181183
write!(f, "Force-skipping unavailable component '{}'", component)
182184
}
183185
SignatureInvalid(url) => write!(f, "Signature verification failed for '{}'", url),
186+
RetryingDownload(url) => write!(f, "Retrying download for '{}'", url),
184187
}
185188
}
186189
}

src/utils/utils.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ pub fn download_file_with_resume(
172172
Ok(_) => Ok(()),
173173
Err(e) => {
174174
let is_client_error = match e.kind() {
175+
// Specifically treat the bad partial range error as not our
176+
// fault in case it was something odd which happened.
177+
ErrorKind::Download(DEK::HttpStatus(416)) => false,
175178
ErrorKind::Download(DEK::HttpStatus(400..=499)) => true,
176179
ErrorKind::Download(DEK::FileNotFound) => true,
177180
_ => false,
@@ -243,11 +246,12 @@ fn download_file_(
243246
(Backend::Reqwest, Notification::UsingReqwest)
244247
};
245248
notify_handler(notification);
246-
download_to_path_with_backend(backend, url, path, resume_from_partial, Some(callback))?;
249+
let res =
250+
download_to_path_with_backend(backend, url, path, resume_from_partial, Some(callback));
247251

248252
notify_handler(Notification::DownloadFinished);
249253

250-
Ok(())
254+
res.map_err(|e| e.into())
251255
}
252256

253257
pub fn parse_url(url: &str) -> Result<Url> {

tests/dist.rs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2018,30 +2018,13 @@ fn handle_corrupt_partial_downloads() {
20182018
)
20192019
.unwrap();
20202020

2021-
let err = update_from_dist(
2022-
url,
2023-
toolchain,
2024-
prefix,
2025-
&[],
2026-
&[],
2027-
download_cfg,
2028-
temp_cfg,
2029-
false,
2030-
)
2031-
.unwrap_err();
2032-
2033-
match err.kind() {
2034-
ErrorKind::ComponentDownloadFailed(_) => (),
2035-
e => panic!("Unexpected error: {:?}", e),
2036-
}
2037-
20382021
update_from_dist(
20392022
url,
20402023
toolchain,
20412024
prefix,
20422025
&[],
20432026
&[],
2044-
&download_cfg,
2027+
download_cfg,
20452028
temp_cfg,
20462029
false,
20472030
)

0 commit comments

Comments
 (0)