Skip to content

Commit c1fb84f

Browse files
committed
src/io/itarbundle.rs: retry HTTP fetches if they fail
Cf. GitHub issue #25. Bintray has transient failures that give a bad user experience, but things will just work if you retry the fetch. So, do that now. Closes #25.
1 parent 4b506df commit c1fb84f

File tree

1 file changed

+39
-8
lines changed

1 file changed

+39
-8
lines changed

src/io/itarbundle.rs

+39-8
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ use std::collections::HashMap;
1313
use std::ffi::{OsStr, OsString};
1414
use std::io::{BufRead, BufReader, Cursor, Read};
1515

16-
use errors::{Error, Result, ResultExt};
16+
use errors::{Error, ErrorKind, Result, ResultExt};
1717
use super::{InputHandle, InputOrigin, IoProvider, OpenResult};
1818
use status::StatusBackend;
1919

2020

21+
const MAX_HTTP_ATTEMPTS: usize = 4;
22+
23+
2124
// A simple way to read chunks out of a big seekable byte stream. You could
2225
// implement this for io::File pretty trivially but that's not currently
2326
// needed.
@@ -146,21 +149,49 @@ impl<F: ITarIoFactory> IoProvider for ITarBundle<F> {
146149
// lifetime-related issues. So for now we just slurp the whole thing
147150
// into RAM.
148151

149-
let info = match self.index.get (name) {
152+
let info = match self.index.get(name) {
150153
Some(i) => i,
151154
None => return OpenResult::NotAvailable,
152155
};
153156

154157
self.factory.report_fetch(name, status);
155158

156-
let mut stream = match self.data.as_mut().unwrap().read_range(info.offset, info.length as usize) {
157-
Ok(r) => r,
158-
Err(e) => return OpenResult::Err(e.into())
159-
};
159+
// When fetching a bunch of resource files (i.e., on the first
160+
// invocation), bintray will sometimes drop connections. The error
161+
// manifests itself in a way that has a not-so-nice user experience.
162+
// Our solution: retry the HTTP a few times in case it was a transient
163+
// problem.
160164

161165
let mut buf = Vec::with_capacity(info.length as usize);
162-
if let Err(e) = stream.read_to_end(&mut buf) {
163-
return OpenResult::Err(e.into());
166+
let mut overall_failed = true;
167+
let mut any_failed = false;
168+
169+
for _ in 0..MAX_HTTP_ATTEMPTS {
170+
let mut stream = match self.data.as_mut().unwrap().read_range(info.offset, info.length as usize) {
171+
Ok(r) => r,
172+
Err(e) => {
173+
tt_warning!(status, "failure requesting \"{}\" from network", name.to_string_lossy(); e.into());
174+
any_failed = true;
175+
continue;
176+
},
177+
};
178+
179+
if let Err(e) = stream.read_to_end(&mut buf) {
180+
tt_warning!(status, "failure downloading \"{}\" from network", name.to_string_lossy(); e.into());
181+
any_failed = true;
182+
continue;
183+
}
184+
185+
overall_failed = false;
186+
break;
187+
}
188+
189+
if overall_failed {
190+
// Note: can't save & reuse the hyper errors since they're not cloneable
191+
return OpenResult::Err(ErrorKind::Msg(format!("failed to retrieve \"{}\" from the network",
192+
name.to_string_lossy())).into());
193+
} else if any_failed {
194+
tt_note!(status, "download succeeded after retry");
164195
}
165196

166197
OpenResult::Ok(InputHandle::new(name, Cursor::new(buf), InputOrigin::Other))

0 commit comments

Comments
 (0)