From 5c05daf75cdc7497096b3fbadfaa4d4a073c893a Mon Sep 17 00:00:00 2001 From: amaanbs Date: Fri, 6 Jun 2025 05:26:51 +0530 Subject: [PATCH 1/2] change binary download distribution --- .../java/com/browserstack/local/Local.java | 4 +- .../com/browserstack/local/LocalBinary.java | 68 ++++++++++++++++--- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/browserstack/local/Local.java b/src/main/java/com/browserstack/local/Local.java index 7669007..6075b02 100644 --- a/src/main/java/com/browserstack/local/Local.java +++ b/src/main/java/com/browserstack/local/Local.java @@ -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(); diff --git a/src/main/java/com/browserstack/local/LocalBinary.java b/src/main/java/com/browserstack/local/LocalBinary.java index 6b7a0fb..4dbd303 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,24 @@ 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 httpPath; + private String binaryFileName; + + 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,7 +44,8 @@ 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); @@ -65,8 +79,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 +180,40 @@ private boolean makePath(String path) { } } + private void fetchSourceUrl() { + 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); + this.sourceUrl = json.getJSONObject("data").getString("endpoint"); + } + } + private void downloadBinary(String destParentDir, Boolean custom) throws LocalException { try { + fetchSourceUrl(); + String source = destParentDir; if (!custom) { if (!new File(destParentDir).exists()) @@ -179,13 +224,20 @@ 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); - + try { + newCopyToFile(url, f); + } catch (IOException e) { + if (fallbackEnabled) throw e; + /* Binary download failed due to a server error */ + fallbackEnabled = true; + downloadFailureThrowable = e; + downloadBinary(destParentDir, custom); + } changePermissions(binaryPath); - } catch (Exception e) { + } catch (Throwable e) { throw new LocalException("Error trying to download BrowserStackLocal binary: " + e.getMessage()); } } From 7094fd61114626fc337336e74fda1494cff0a929 Mon Sep 17 00:00:00 2001 From: amaanbs Date: Fri, 6 Jun 2025 06:21:19 +0530 Subject: [PATCH 2/2] fixes --- .../com/browserstack/local/LocalBinary.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/browserstack/local/LocalBinary.java b/src/main/java/com/browserstack/local/LocalBinary.java index 4dbd303..c6d5254 100644 --- a/src/main/java/com/browserstack/local/LocalBinary.java +++ b/src/main/java/com/browserstack/local/LocalBinary.java @@ -47,12 +47,23 @@ class LocalBinary { 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; + fallbackEnabled = true; + downloadFailureThrowable = e; + downloadAndVerifyBinary(path); } - checkBinary(); } private void initialize() throws LocalException { @@ -181,6 +192,11 @@ private boolean makePath(String path) { } private void fetchSourceUrl() { + if ((!fallbackEnabled && sourceUrl) || (fallbackEnabled && !downloadFailureThrowable)) { + /* Retry because binary (from any of the endpoints) validation failed */ + return; + } + URL url = new URL("https://local.browserstack.com/binary/api/v1/endpoint"); URLConnection connection = url.openConnection(); @@ -207,6 +223,7 @@ private void fetchSourceUrl() { String responseBody = response.toString(); JSONObject json = new JSONObject(responseBody); this.sourceUrl = json.getJSONObject("data").getString("endpoint"); + if(fallbackEnabled) downloadFailureThrowable = null; } } @@ -227,15 +244,8 @@ private void downloadBinary(String destParentDir, Boolean custom) throws LocalEx URL url = new URL(sourceUrl + '/' + binaryFileName); File f = new File(source); - try { - newCopyToFile(url, f); - } catch (IOException e) { - if (fallbackEnabled) throw e; - /* Binary download failed due to a server error */ - fallbackEnabled = true; - downloadFailureThrowable = e; - downloadBinary(destParentDir, custom); - } + newCopyToFile(url, f); + changePermissions(binaryPath); } catch (Throwable e) { throw new LocalException("Error trying to download BrowserStackLocal binary: " + e.getMessage());