@@ -13,11 +13,14 @@ use std::collections::HashMap;
13
13
use std:: ffi:: { OsStr , OsString } ;
14
14
use std:: io:: { BufRead , BufReader , Cursor , Read } ;
15
15
16
- use errors:: { Error , Result , ResultExt } ;
16
+ use errors:: { Error , ErrorKind , Result , ResultExt } ;
17
17
use super :: { InputHandle , InputOrigin , IoProvider , OpenResult } ;
18
18
use status:: StatusBackend ;
19
19
20
20
21
+ const MAX_HTTP_ATTEMPTS : usize = 4 ;
22
+
23
+
21
24
// A simple way to read chunks out of a big seekable byte stream. You could
22
25
// implement this for io::File pretty trivially but that's not currently
23
26
// needed.
@@ -146,21 +149,49 @@ impl<F: ITarIoFactory> IoProvider for ITarBundle<F> {
146
149
// lifetime-related issues. So for now we just slurp the whole thing
147
150
// into RAM.
148
151
149
- let info = match self . index . get ( name) {
152
+ let info = match self . index . get ( name) {
150
153
Some ( i) => i,
151
154
None => return OpenResult :: NotAvailable ,
152
155
} ;
153
156
154
157
self . factory . report_fetch ( name, status) ;
155
158
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.
160
164
161
165
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" ) ;
164
195
}
165
196
166
197
OpenResult :: Ok ( InputHandle :: new ( name, Cursor :: new ( buf) , InputOrigin :: Other ) )
0 commit comments