3
3
import org .apache .commons .io .FileUtils ;
4
4
import org .apache .commons .io .IOUtils ;
5
5
6
+ import org .json .JSONObject ;
7
+
6
8
import java .io .IOException ;
7
9
import java .io .InputStream ;
10
+ import java .io .OutputStream ;
8
11
import java .io .BufferedReader ;
9
12
import java .io .InputStreamReader ;
10
13
import java .io .File ;
15
18
import java .util .zip .GZIPInputStream ;
16
19
import java .util .zip .ZipException ;
17
20
21
+ import java .lang .StringBuilder ;
22
+
18
23
class LocalBinary {
19
24
20
- private static final String BIN_URL = "https://www.browserstack.com/local-testing/downloads/binaries/" ;
25
+ private String binaryFileName ;
21
26
22
- private String httpPath ;
27
+ private String sourceUrl ;
23
28
24
29
private String binaryPath ;
25
30
31
+ private Boolean fallbackEnabled = false ;
32
+
33
+ private Throwable downloadFailureThrowable = null ;
34
+
35
+ private String key ;
36
+
26
37
private boolean isOSWindows ;
27
38
28
39
private final String orderedPaths [] = {
@@ -31,14 +42,30 @@ class LocalBinary {
31
42
System .getProperty ("java.io.tmpdir" )
32
43
};
33
44
34
- LocalBinary (String path ) throws LocalException {
45
+ LocalBinary (String path , String key ) throws LocalException {
46
+ this .key = key ;
35
47
initialize ();
36
- if (path != "" ) {
37
- getBinaryOnPath (path );
38
- } else {
39
- getBinary ();
48
+ downloadAndVerifyBinary (path );
49
+ }
50
+
51
+ private void downloadAndVerifyBinary (String path ) throws LocalException {
52
+ try {
53
+ if (path != "" ) {
54
+ getBinaryOnPath (path );
55
+ } else {
56
+ getBinary ();
57
+ }
58
+ checkBinary ();
59
+ } catch (Throwable e ) {
60
+ if (fallbackEnabled ) throw e ;
61
+ File binary_file = new File (binaryPath );
62
+ if (binary_file .exists ()) {
63
+ binary_file .delete ();
64
+ }
65
+ fallbackEnabled = true ;
66
+ downloadFailureThrowable = e ;
67
+ downloadAndVerifyBinary (path );
40
68
}
41
- checkBinary ();
42
69
}
43
70
44
71
private void initialize () throws LocalException {
@@ -65,8 +92,7 @@ private void initialize() throws LocalException {
65
92
throw new LocalException ("Failed to detect OS type" );
66
93
}
67
94
68
- String sourceURL = BIN_URL ;
69
- httpPath = sourceURL + binFileName ;
95
+ this .binaryFileName = binFileName ;
70
96
}
71
97
72
98
private boolean isAlpine () {
@@ -167,8 +193,53 @@ private boolean makePath(String path) {
167
193
}
168
194
}
169
195
196
+ private void fetchSourceUrl () throws LocalException {
197
+ if ((!fallbackEnabled && sourceUrl != null ) || (fallbackEnabled && downloadFailureThrowable == null )) {
198
+ /* Retry because binary (from any of the endpoints) validation failed */
199
+ return ;
200
+ }
201
+
202
+ try {
203
+ URL url = new URL ("https://local.browserstack.com/binary/api/v1/endpoint" );
204
+ URLConnection connection = url .openConnection ();
205
+
206
+ connection .setDoOutput (true );
207
+ connection .setRequestProperty ("Content-Type" , "application/json" );
208
+ connection .setRequestProperty ("User-Agent" , "browserstack-local-java/" + Local .getPackageVersion ());
209
+ connection .setRequestProperty ("Accept" , "application/json" );
210
+ if (fallbackEnabled ) connection .setRequestProperty ("X-Local-Fallback-Cloudflare" , "true" );
211
+
212
+ String jsonInput = "{\" auth_token\" : \" " + key + (fallbackEnabled ? ("\" , \" error_message\" : \" " + downloadFailureThrowable .getMessage ()) + "\" " : "\" " ) + "}" ;
213
+
214
+ try (OutputStream os = connection .getOutputStream ()) {
215
+ byte [] input = jsonInput .getBytes ("utf-8" );
216
+ os .write (input , 0 , input .length );
217
+ }
218
+
219
+ try (InputStream is = connection .getInputStream ();
220
+ BufferedReader reader = new BufferedReader (new InputStreamReader (is , "utf-8" ))) {
221
+ StringBuilder response = new StringBuilder ();
222
+ String line ;
223
+ while ((line = reader .readLine ()) != null ) {
224
+ response .append (line .trim ());
225
+ }
226
+ String responseBody = response .toString ();
227
+ JSONObject json = new JSONObject (responseBody );
228
+ if (json .has ("error" )) {
229
+ throw new Exception (json .getString ("error" ));
230
+ }
231
+ this .sourceUrl = json .getJSONObject ("data" ).getString ("endpoint" );
232
+ if (fallbackEnabled ) downloadFailureThrowable = null ;
233
+ }
234
+ } catch (Throwable e ) {
235
+ throw new LocalException ("Error trying to fetch the source URL: " + e .getMessage ());
236
+ }
237
+ }
238
+
170
239
private void downloadBinary (String destParentDir , Boolean custom ) throws LocalException {
171
240
try {
241
+ fetchSourceUrl ();
242
+
172
243
String source = destParentDir ;
173
244
if (!custom ) {
174
245
if (!new File (destParentDir ).exists ())
@@ -179,13 +250,13 @@ private void downloadBinary(String destParentDir, Boolean custom) throws LocalEx
179
250
source += ".exe" ;
180
251
}
181
252
}
182
- URL url = new URL (httpPath );
253
+ URL url = new URL (sourceUrl + '/' + binaryFileName );
183
254
184
255
File f = new File (source );
185
256
newCopyToFile (url , f );
186
257
187
258
changePermissions (binaryPath );
188
- } catch (Exception e ) {
259
+ } catch (Throwable e ) {
189
260
throw new LocalException ("Error trying to download BrowserStackLocal binary: " + e .getMessage ());
190
261
}
191
262
}
@@ -235,3 +306,4 @@ private static void customCopyInputStreamToFile(InputStream stream, File file, U
235
306
}
236
307
}
237
308
}
309
+
0 commit comments