Skip to content

Commit d1b851f

Browse files
committed
Auto merge of #1131 - jelford:download_cleanup, r=Diggsey
Cleanup download-related code in the rustup_dist crate This PR is probably easiest to read if you do it commit-wise: the first commit just removes the old, unused `download` module from rustup_dist. The second commit pulls `DownloadCfg` (and friends) out into a _new_ `download` module. The only other change in there is that I've tried to kill some of the instances where the `notify_handler` gets needlessly passed around by moving functions to be methods on `DownloadCfg` + using the field it's already got. I suspect the only reason these weren't done before is that it was difficult to reason about the code, so hopefully this is an improvement. There aren't any (intentional) functional changes in this, so I haven't made any changes to the tests (aside from those required to get it to compile). No rush to get this merged for the same reason.
2 parents 5d67261 + 61f641a commit d1b851f

File tree

6 files changed

+253
-312
lines changed

6 files changed

+253
-312
lines changed

src/rustup-dist/src/dist.rs

Lines changed: 4 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,15 @@ use prefix::InstallPrefix;
77
use manifest::Component;
88
use manifest::Manifest as ManifestV2;
99
use manifestation::{Manifestation, UpdateStatus, Changes};
10+
use download::{DownloadCfg};
1011

11-
use std::path::{Path, PathBuf};
12-
use std::ops;
13-
use url::Url;
12+
use std::path::Path;
1413
use std::fmt;
1514
use std::env;
16-
use std::fs;
1715

1816
use regex::Regex;
19-
use sha2::{Sha256, Digest};
2017

2118
pub const DEFAULT_DIST_SERVER: &'static str = "https://static.rust-lang.org";
22-
pub const UPDATE_HASH_LEN: usize = 20;
2319

2420
// Deprecated
2521
pub const DEFAULT_DIST_ROOT: &'static str = "https://static.rust-lang.org/dist";
@@ -440,168 +436,6 @@ impl fmt::Display for ToolchainDesc {
440436
}
441437
}
442438

443-
pub fn download_and_check<'a>(url_str: &str,
444-
update_hash: Option<&Path>,
445-
ext: &str,
446-
cfg: DownloadCfg<'a>)
447-
-> Result<Option<(temp::File<'a>, String)>> {
448-
let hash = try!(download_hash(url_str, cfg));
449-
let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect();
450-
451-
if let Some(hash_file) = update_hash {
452-
if utils::is_file(hash_file) {
453-
if let Ok(contents) = utils::read_file("update hash", hash_file) {
454-
if contents == partial_hash {
455-
// Skip download, update hash matches
456-
return Ok(None);
457-
}
458-
} else {
459-
(cfg.notify_handler)(Notification::CantReadUpdateHash(hash_file));
460-
}
461-
} else {
462-
(cfg.notify_handler)(Notification::NoUpdateHash(hash_file));
463-
}
464-
}
465-
466-
let url = try!(utils::parse_url(url_str));
467-
let file = try!(cfg.temp_cfg.new_file_with_ext("", ext));
468-
469-
let mut hasher = Sha256::new();
470-
try!(utils::download_file(&url,
471-
&file,
472-
Some(&mut hasher),
473-
&|n| (cfg.notify_handler)(n.into())));
474-
let actual_hash = hasher.result_str();
475-
476-
if hash != actual_hash {
477-
// Incorrect hash
478-
return Err(ErrorKind::ChecksumFailed {
479-
url: url_str.to_owned(),
480-
expected: hash,
481-
calculated: actual_hash,
482-
}
483-
.into());
484-
} else {
485-
(cfg.notify_handler)(Notification::ChecksumValid(url_str));
486-
}
487-
488-
// TODO: Check the signature of the file
489-
490-
Ok(Some((file, partial_hash)))
491-
}
492-
493-
#[derive(Copy, Clone)]
494-
pub struct DownloadCfg<'a> {
495-
pub dist_root: &'a str,
496-
pub temp_cfg: &'a temp::Cfg,
497-
pub download_dir: &'a PathBuf,
498-
pub notify_handler: &'a Fn(Notification),
499-
}
500-
501-
502-
pub struct File {
503-
path: PathBuf,
504-
}
505-
506-
impl ops::Deref for File {
507-
type Target = Path;
508-
509-
fn deref(&self) -> &Path {
510-
ops::Deref::deref(&self.path)
511-
}
512-
}
513-
514-
fn file_hash(path: &Path) -> Result<String> {
515-
let mut hasher = Sha256::new();
516-
use std::io::Read;
517-
let mut downloaded = try!(fs::File::open(&path).chain_err(|| "opening already downloaded file"));
518-
let mut buf = vec![0; 32768];
519-
loop {
520-
if let Ok(n) = downloaded.read(&mut buf) {
521-
if n == 0 { break; }
522-
hasher.input(&buf[..n]);
523-
} else {
524-
break;
525-
}
526-
}
527-
528-
Ok(hasher.result_str())
529-
}
530-
531-
impl<'a> DownloadCfg<'a> {
532-
533-
pub fn download(&self, url: &Url, hash: &str, notify_handler: &'a Fn(Notification)) -> Result<File> {
534-
535-
try!(utils::ensure_dir_exists("Download Directory", &self.download_dir, &|n| notify_handler(n.into())));
536-
let target_file = self.download_dir.join(Path::new(hash));
537-
538-
if target_file.exists() {
539-
let cached_result = try!(file_hash(&target_file));
540-
if hash == cached_result {
541-
notify_handler(Notification::FileAlreadyDownloaded);
542-
notify_handler(Notification::ChecksumValid(&url.to_string()));
543-
return Ok(File { path: target_file, });
544-
} else {
545-
notify_handler(Notification::CachedFileChecksumFailed);
546-
try!(fs::remove_file(&target_file).chain_err(|| "cleaning up previous download"));
547-
}
548-
}
549-
550-
551-
let partial_file_path =
552-
target_file.with_file_name(
553-
target_file.file_name().map(|s| {
554-
s.to_str().unwrap_or("_")})
555-
.unwrap_or("_")
556-
.to_owned()
557-
+ ".partial");
558-
559-
let mut hasher = Sha256::new();
560-
561-
try!(utils::download_file_with_resume(&url,
562-
&partial_file_path,
563-
Some(&mut hasher),
564-
true,
565-
&|n| notify_handler(n.into())));
566-
567-
let actual_hash = hasher.result_str();
568-
569-
if hash != actual_hash {
570-
// Incorrect hash
571-
return Err(ErrorKind::ChecksumFailed {
572-
url: url.to_string(),
573-
expected: hash.to_string(),
574-
calculated: actual_hash,
575-
}.into());
576-
} else {
577-
notify_handler(Notification::ChecksumValid(&url.to_string()));
578-
try!(fs::rename(&partial_file_path, &target_file));
579-
return Ok(File { path: target_file });
580-
}
581-
}
582-
583-
pub fn clean(&self, hashes: &Vec<String>) -> Result<()> {
584-
for hash in hashes.iter() {
585-
let used_file = self.download_dir.join(hash);
586-
if self.download_dir.join(&used_file).exists() {
587-
try!(fs::remove_file(used_file).chain_err(|| "cleaning up cached downloads"));
588-
}
589-
}
590-
Ok(())
591-
}
592-
}
593-
594-
pub fn download_hash(url: &str, cfg: DownloadCfg) -> Result<String> {
595-
let hash_url = try!(utils::parse_url(&(url.to_owned() + ".sha256")));
596-
let hash_file = try!(cfg.temp_cfg.new_file());
597-
598-
try!(utils::download_file(&hash_url,
599-
&hash_file,
600-
None,
601-
&|n| (cfg.notify_handler)(n.into())));
602-
603-
Ok(try!(utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned())))
604-
}
605439

606440
// Installs or updates a toolchain from a dist server. If an initial
607441
// install then it will be installed with the default components. If
@@ -711,7 +545,7 @@ fn dl_v2_manifest<'a>(download: DownloadCfg<'a>,
711545
toolchain: &ToolchainDesc)
712546
-> Result<Option<(ManifestV2, String)>> {
713547
let manifest_url = toolchain.manifest_v2_url(download.dist_root);
714-
let manifest_dl_res = download_and_check(&manifest_url, update_hash, ".toml", download);
548+
let manifest_dl_res = download.download_and_check(&manifest_url, update_hash, ".toml");
715549

716550
if let Ok(manifest_dl) = manifest_dl_res {
717551
// Downloaded ok!
@@ -751,7 +585,7 @@ fn dl_v1_manifest<'a>(download: DownloadCfg<'a>, toolchain: &ToolchainDesc) -> R
751585
}
752586

753587
let manifest_url = toolchain.manifest_v1_url(download.dist_root);
754-
let manifest_dl = try!(download_and_check(&manifest_url, None, "", download));
588+
let manifest_dl = try!(download.download_and_check(&manifest_url, None, ""));
755589
let (manifest_file, _) = manifest_dl.unwrap();
756590
let manifest_str = try!(utils::read_file("manifest", &manifest_file));
757591
let urls = manifest_str.lines().map(|s| format!("{}/{}", root_url, s)).collect();

0 commit comments

Comments
 (0)