diff --git a/README.md b/README.md
index 971be2e..fd71907 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Add this dependency to your project's POM:
com.browserstack
browserstack-local-java
- 1.1.5
+ 1.1.6
```
diff --git a/pom.xml b/pom.xml
index c372100..b59d098 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
com.browserstack
browserstack-local-java
jar
- 1.1.5
+ 1.1.6
browserstack-local-java
Java bindings for BrowserStack Local
diff --git a/src/main/java/com/browserstack/local/Local.java b/src/main/java/com/browserstack/local/Local.java
index 7669007..92ac38c 100644
--- a/src/main/java/com/browserstack/local/Local.java
+++ b/src/main/java/com/browserstack/local/Local.java
@@ -23,7 +23,7 @@ public class Local {
private LocalProcess proc = null;
// Current version of binding package, used for --source option of binary
- private static final String packageVersion = "1.1.5";
+ private static final String packageVersion = "1.1.6";
private final Map parameters;
private final Map avoidValueParameters;
@@ -55,9 +55,9 @@ public void start(Map options) throws Exception {
startOptions = options;
LocalBinary lb;
if (options.get("binarypath") != null) {
- lb = new LocalBinary(options.get("binarypath"));
+ lb = new LocalBinary(options.get("binarypath"), options.get("key"));
} else {
- lb = new LocalBinary("");
+ lb = new LocalBinary("", options.get("key"));
}
binaryPath = lb.getBinaryPath();
@@ -109,9 +109,9 @@ public void stop() throws Exception {
public void stop(Map options) throws Exception {
LocalBinary lb;
if (options.get("binarypath") != null) {
- lb = new LocalBinary(options.get("binarypath"));
+ lb = new LocalBinary(options.get("binarypath"), options.get("key"));
} else {
- lb = new LocalBinary("");
+ lb = new LocalBinary("", options.get("key"));
}
binaryPath = lb.getBinaryPath();
makeCommand(options, "stop");
diff --git a/src/main/java/com/browserstack/local/LocalBinary.java b/src/main/java/com/browserstack/local/LocalBinary.java
index 6b7a0fb..08af9ad 100644
--- a/src/main/java/com/browserstack/local/LocalBinary.java
+++ b/src/main/java/com/browserstack/local/LocalBinary.java
@@ -3,8 +3,11 @@
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
+import org.json.JSONObject;
+
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.File;
@@ -15,14 +18,22 @@
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipException;
+import java.lang.StringBuilder;
+
class LocalBinary {
- private static final String BIN_URL = "https://www.browserstack.com/local-testing/downloads/binaries/";
+ private String binaryFileName;
- private String httpPath;
+ private String sourceUrl;
private String binaryPath;
+ private Boolean fallbackEnabled = false;
+
+ private Throwable downloadFailureThrowable = null;
+
+ private String key;
+
private boolean isOSWindows;
private final String orderedPaths[] = {
@@ -31,14 +42,30 @@ class LocalBinary {
System.getProperty("java.io.tmpdir")
};
- LocalBinary(String path) throws LocalException {
+ LocalBinary(String path, String key) throws LocalException {
+ this.key = key;
initialize();
- if (path != "") {
- getBinaryOnPath(path);
- } else {
- getBinary();
+ downloadAndVerifyBinary(path);
+ }
+
+ private void downloadAndVerifyBinary(String path) throws LocalException {
+ try {
+ if (path != "") {
+ getBinaryOnPath(path);
+ } else {
+ getBinary();
+ }
+ checkBinary();
+ } catch (Throwable e) {
+ if (fallbackEnabled) throw e;
+ File binary_file = new File(binaryPath);
+ if (binary_file.exists()) {
+ binary_file.delete();
+ }
+ fallbackEnabled = true;
+ downloadFailureThrowable = e;
+ downloadAndVerifyBinary(path);
}
- checkBinary();
}
private void initialize() throws LocalException {
@@ -65,8 +92,7 @@ private void initialize() throws LocalException {
throw new LocalException("Failed to detect OS type");
}
- String sourceURL = BIN_URL;
- httpPath = sourceURL + binFileName;
+ this.binaryFileName = binFileName;
}
private boolean isAlpine() {
@@ -167,8 +193,53 @@ private boolean makePath(String path) {
}
}
+ private void fetchSourceUrl() throws LocalException {
+ if ((!fallbackEnabled && sourceUrl != null) || (fallbackEnabled && downloadFailureThrowable == null)) {
+ /* Retry because binary (from any of the endpoints) validation failed */
+ return;
+ }
+
+ try {
+ URL url = new URL("https://local.browserstack.com/binary/api/v1/endpoint");
+ URLConnection connection = url.openConnection();
+
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Content-Type", "application/json");
+ connection.setRequestProperty("User-Agent", "browserstack-local-java/" + Local.getPackageVersion());
+ connection.setRequestProperty("Accept", "application/json");
+ if (fallbackEnabled) connection.setRequestProperty("X-Local-Fallback-Cloudflare", "true");
+
+ String jsonInput = "{\"auth_token\": \"" + key + (fallbackEnabled ? ("\", \"error_message\": \"" + downloadFailureThrowable.getMessage()) + "\"" : "\"") + "}";
+
+ try (OutputStream os = connection.getOutputStream()) {
+ byte[] input = jsonInput.getBytes("utf-8");
+ os.write(input, 0, input.length);
+ }
+
+ try (InputStream is = connection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8"))) {
+ StringBuilder response = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line.trim());
+ }
+ String responseBody = response.toString();
+ JSONObject json = new JSONObject(responseBody);
+ if (json.has("error")) {
+ throw new Exception(json.getString("error"));
+ }
+ this.sourceUrl = json.getJSONObject("data").getString("endpoint");
+ if(fallbackEnabled) downloadFailureThrowable = null;
+ }
+ } catch (Throwable e) {
+ throw new LocalException("Error trying to fetch the source URL: " + e.getMessage());
+ }
+ }
+
private void downloadBinary(String destParentDir, Boolean custom) throws LocalException {
try {
+ fetchSourceUrl();
+
String source = destParentDir;
if (!custom) {
if (!new File(destParentDir).exists())
@@ -179,13 +250,13 @@ private void downloadBinary(String destParentDir, Boolean custom) throws LocalEx
source += ".exe";
}
}
- URL url = new URL(httpPath);
+ URL url = new URL(sourceUrl + '/' + binaryFileName);
File f = new File(source);
newCopyToFile(url, f);
changePermissions(binaryPath);
- } catch (Exception e) {
+ } catch (Throwable e) {
throw new LocalException("Error trying to download BrowserStackLocal binary: " + e.getMessage());
}
}
@@ -235,3 +306,4 @@ private static void customCopyInputStreamToFile(InputStream stream, File file, U
}
}
}
+