Skip to content

Commit c05fd7d

Browse files
authored
Merge pull request #54 from browserstack/Change_Binary_Download_Distribution
Change Binary Download Distribution
2 parents 227ad4a + 79d48d0 commit c05fd7d

File tree

5 files changed

+138
-47
lines changed

5 files changed

+138
-47
lines changed

BrowserStackLocal/BrowserStackLocal Unit Tests/BrowserStackTunnelTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ public void TestInitialState()
3030
public void TestBinaryPathIsSet()
3131
{
3232
tunnel = new TunnelClass();
33-
tunnel.addBinaryPath("dummyPath");
33+
tunnel.addBinaryPath("dummyPath", "");
3434
Assert.AreEqual(tunnel.getBinaryAbsolute(), "dummyPath");
3535
}
3636
[TestMethod]
3737
public void TestBinaryPathOnNull()
3838
{
3939
tunnel = new TunnelClass();
40-
tunnel.addBinaryPath(null);
40+
tunnel.addBinaryPath(null, "");
4141
string expectedPath = Path.Combine(homepath, ".browserstack");
4242
expectedPath = Path.Combine(expectedPath, binaryName);
4343
Assert.AreEqual(tunnel.getBinaryAbsolute(), expectedPath);
@@ -46,7 +46,7 @@ public void TestBinaryPathOnNull()
4646
public void TestBinaryPathOnEmpty()
4747
{
4848
tunnel = new TunnelClass();
49-
tunnel.addBinaryPath("");
49+
tunnel.addBinaryPath("", "");
5050
string expectedPath = Path.Combine(homepath, ".browserstack");
5151
expectedPath = Path.Combine(expectedPath, binaryName);
5252
Assert.AreEqual(tunnel.getBinaryAbsolute(), expectedPath);
@@ -56,7 +56,7 @@ public void TestBinaryPathOnFallback()
5656
{
5757
string expectedPath = "dummyPath";
5858
tunnel = new TunnelClass();
59-
tunnel.addBinaryPath("dummyPath");
59+
tunnel.addBinaryPath("dummyPath", "");
6060
Assert.AreEqual(tunnel.getBinaryAbsolute(), expectedPath);
6161

6262
tunnel.fallbackPaths();
@@ -78,7 +78,7 @@ public void TestBinaryPathOnFallback()
7878
public void TestBinaryPathOnNoMoreFallback()
7979
{
8080
tunnel = new TunnelClass();
81-
tunnel.addBinaryPath("dummyPath");
81+
tunnel.addBinaryPath("dummyPath", "");
8282
tunnel.fallbackPaths();
8383
tunnel.fallbackPaths();
8484
tunnel.fallbackPaths();

BrowserStackLocal/BrowserStackLocal Unit Tests/LocalTests.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public void TestWorksForBinaryPath()
107107
tunnelMock.Setup(mock => mock.Run("dummyKey", "", logAbsolute, "start"));
108108
local.setTunnel(tunnelMock.Object);
109109
local.start(options);
110-
tunnelMock.Verify(mock => mock.addBinaryPath("dummyPath"), Times.Once);
110+
tunnelMock.Verify(mock => mock.addBinaryPath("dummyPath", ""), Times.Once);
111111
tunnelMock.Verify(mock => mock.addBinaryArguments(It.IsRegex("-logFile \"" + logAbsolute + "\" .*")), Times.Once());
112112
tunnelMock.Verify(mock => mock.Run("dummyKey", "", logAbsolute, "start"), Times.Once());
113113
local.stop();
@@ -129,7 +129,7 @@ public void TestWorksWithBooleanOptions()
129129
tunnelMock.Setup(mock => mock.Run("dummyKey", "", logAbsolute, "start"));
130130
local.setTunnel(tunnelMock.Object);
131131
local.start(options);
132-
tunnelMock.Verify(mock => mock.addBinaryPath(""), Times.Once);
132+
tunnelMock.Verify(mock => mock.addBinaryPath("", ""), Times.Once);
133133
tunnelMock.Verify(mock => mock.addBinaryArguments(It.IsRegex("-vvv.*-force.*-forcelocal.*-forceproxy.*-onlyAutomate.*")), Times.Once());
134134
tunnelMock.Verify(mock => mock.Run("dummyKey", "", logAbsolute, "start"), Times.Once());
135135
local.stop();
@@ -152,7 +152,7 @@ public void TestWorksWithValueOptions()
152152
tunnelMock.Setup(mock =>mock.Run("dummyKey", "", logAbsolute, "start"));
153153
local.setTunnel(tunnelMock.Object);
154154
local.start(options);
155-
tunnelMock.Verify(mock => mock.addBinaryPath(""), Times.Once);
155+
tunnelMock.Verify(mock => mock.addBinaryPath("", ""), Times.Once);
156156
tunnelMock.Verify(mock => mock.addBinaryArguments(
157157
It.IsRegex("-localIdentifier.*dummyIdentifier.*dummyHost.*-proxyHost.*dummyHost.*-proxyPort.*dummyPort.*-proxyUser.*dummyUser.*-proxyPass.*dummyPass.*")
158158
), Times.Once());
@@ -175,7 +175,7 @@ public void TestWorksWithCustomOptions()
175175
tunnelMock.Setup(mock => mock.Run("dummyKey", "", logAbsolute, "start"));
176176
local.setTunnel(tunnelMock.Object);
177177
local.start(options);
178-
tunnelMock.Verify(mock => mock.addBinaryPath(""), Times.Once);
178+
tunnelMock.Verify(mock => mock.addBinaryPath("", ""), Times.Once);
179179
tunnelMock.Verify(mock => mock.addBinaryArguments(
180180
It.IsRegex("-customBoolKey1.*-customBoolKey2.*-customKey1.*customValue1.*-customKey2.*customValue2.*")
181181
), Times.Once());
@@ -200,7 +200,7 @@ public void TestCallsFallbackOnFailure()
200200
});
201201
local.setTunnel(tunnelMock.Object);
202202
local.start(options);
203-
tunnelMock.Verify(mock => mock.addBinaryPath(""), Times.Once);
203+
tunnelMock.Verify(mock => mock.addBinaryPath("", ""), Times.Once);
204204
tunnelMock.Verify(mock => mock.addBinaryArguments(It.IsRegex("-logFile \"" + logAbsolute + "\" .*")), Times.Once());
205205
tunnelMock.Verify(mock => mock.Run("dummyKey", "", logAbsolute, "start"), Times.Exactly(2));
206206
tunnelMock.Verify(mock => mock.fallbackPaths(), Times.Once());
@@ -219,7 +219,7 @@ public void TestKillsTunnel()
219219
local.setTunnel(tunnelMock.Object);
220220
local.start(options);
221221
local.stop();
222-
tunnelMock.Verify(mock => mock.addBinaryPath(""), Times.Once);
222+
tunnelMock.Verify(mock => mock.addBinaryPath("", ""), Times.Once);
223223
tunnelMock.Verify(mock => mock.addBinaryArguments(It.IsRegex("-logFile \"" + logAbsolute + "\" .*")), Times.Once());
224224
tunnelMock.Verify(mock => mock.Run("dummyKey", "", logAbsolute, "start"), Times.Once());
225225
}

BrowserStackLocal/BrowserStackLocal/BrowserStackLocal.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<RootNamespace>BrowserStack</RootNamespace>
44
<AssemblyName>BrowserStackLocal</AssemblyName>
5-
<TargetFrameworks>net20;netstandard2.0</TargetFrameworks>
5+
<TargetFramework>netstandard2.0</TargetFramework>
66
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
77
<Title>BrowserStackLocal</Title>
88
<Product>BrowserStackLocal</Product>

BrowserStackLocal/BrowserStackLocal/BrowserStackTunnel.cs

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
using System.Security.Principal;
99
using Newtonsoft.Json.Linq;
1010

11+
12+
using System.Collections.Generic;
13+
using System.Net.Http;
14+
using System.Threading.Tasks;
15+
using Newtonsoft.Json;
16+
1117
namespace BrowserStack
1218
{
1319
public enum LocalState { Idle, Connecting, Connected, Error, Disconnected };
@@ -16,7 +22,11 @@ public class BrowserStackTunnel : IDisposable
1622
{
1723
static readonly string uname = Util.GetUName();
1824
static readonly string binaryName = GetBinaryName();
19-
static readonly string downloadURL = "https://www.browserstack.com/local-testing/downloads/binaries/" + binaryName;
25+
26+
private static string userAgent = "";
27+
private string sourceUrl = null;
28+
private bool isFallbackEnabled = false;
29+
private Exception downloadFailureException = null;
2030

2131
static readonly string homepath = !IsWindows() ?
2232
Environment.GetFolderPath(Environment.SpecialFolder.Personal) :
@@ -41,7 +51,7 @@ static bool IsDarwin(string osName)
4151
{
4252
return osName.Contains("darwin");
4353
}
44-
54+
4555

4656
static bool IsWindows()
4757
{
@@ -84,8 +94,15 @@ static string GetBinaryName()
8494
return "BrowserStackLocal.exe";
8595
}
8696

87-
public virtual void addBinaryPath(string binaryAbsolute)
97+
public virtual void addBinaryPath(string binaryAbsolute, string accessKey, bool fallbackEnabled = false, Exception failureException = null)
8898
{
99+
if (basePathsIndex == -1)
100+
{
101+
/* Called at most twice (primary & a fallback) */
102+
isFallbackEnabled = fallbackEnabled;
103+
downloadFailureException = failureException;
104+
fetchSourceUrl(accessKey);
105+
}
89106
if (binaryAbsolute == null || binaryAbsolute.Trim().Length == 0)
90107
{
91108
binaryAbsolute = Path.Combine(basePaths[++basePathsIndex], binaryName);
@@ -102,14 +119,19 @@ public virtual void addBinaryArguments(string binaryArguments)
102119
this.binaryArguments = binaryArguments;
103120
}
104121

105-
public BrowserStackTunnel()
122+
public BrowserStackTunnel(string userAgentParam)
106123
{
124+
userAgent = userAgentParam;
107125
localState = LocalState.Idle;
108126
output = new StringBuilder();
109127
}
110128

111129
public virtual void fallbackPaths()
112130
{
131+
if (File.Exists(binaryAbsolute))
132+
{
133+
File.Delete(binaryAbsolute);
134+
}
113135
if (basePathsIndex >= basePaths.Length - 1)
114136
{
115137
throw new Exception("Binary not found or failed to launch. Make sure that BrowserStackLocal is not already running.");
@@ -121,7 +143,7 @@ public virtual void fallbackPaths()
121143
public void modifyBinaryPermission()
122144
{
123145
if (!IsWindows())
124-
{
146+
{
125147
try
126148
{
127149
using (Process proc = Process.Start("/bin/bash", $"-c \"chmod 0755 {this.binaryAbsolute}\""))
@@ -143,16 +165,58 @@ public void modifyBinaryPermission()
143165
}
144166
}
145167

168+
private string fetchSourceUrl(string accessKey)
169+
{
170+
var url = "https://local.browserstack.com/binary/api/v1/endpoint";
171+
172+
using (var client = new HttpClient())
173+
{
174+
var data = new Dictionary<string, object>
175+
{
176+
{ "auth_token", accessKey }
177+
};
178+
179+
if (isFallbackEnabled)
180+
{
181+
data["error_message"] = downloadFailureException.Message;
182+
}
183+
184+
var jsonData = JsonConvert.SerializeObject(data);
185+
var content = new StringContent(jsonData, Encoding.UTF8, "application/json");
186+
187+
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
188+
if (isFallbackEnabled)
189+
{
190+
client.DefaultRequestHeaders.Add("X-Local-Fallback-Cloudflare", "true");
191+
}
192+
193+
var response = client.PostAsync(url, content).Result;
194+
195+
response.EnsureSuccessStatusCode();
196+
197+
var responseString = response.Content.ReadAsStringAsync().Result;
198+
199+
var jsonResponse = JObject.Parse(responseString);
200+
201+
if (jsonResponse["error"] != null)
202+
{
203+
throw new Exception((string)jsonResponse["error"]);
204+
}
205+
206+
sourceUrl = jsonResponse["data"]?["endpoint"]?.ToString();
207+
return sourceUrl;
208+
}
209+
}
210+
146211
public void downloadBinary()
147212
{
148213
string binaryDirectory = Path.Combine(this.binaryAbsolute, "..");
149-
//string binaryAbsolute = Path.Combine(binaryDirectory, binaryName);
150214

151215
Directory.CreateDirectory(binaryDirectory);
152216

153217
using (var client = new WebClient())
154218
{
155-
client.DownloadFile(downloadURL, this.binaryAbsolute);
219+
client.DownloadFile(sourceUrl + "/" + binaryName, this.binaryAbsolute);
156220
}
157221

158222
if (!File.Exists(binaryAbsolute))

BrowserStackLocal/BrowserStackLocal/Local.cs

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ public class Local
1414
private string argumentString = "";
1515
private string customBinaryPath = "";
1616
private string bindingVersion = "";
17+
private string userAgent = "browserstack-local-csharp";
18+
private bool isFallbackEnabled = false;
19+
private Exception downloadFailureException = null;
1720

1821
protected BrowserStackTunnel tunnel = null;
1922
private static KeyValuePair<string, string> emptyStringPair = new KeyValuePair<string, string>();
@@ -144,11 +147,59 @@ public static string GetVersionStringFromAssemblyEmbedded(string pAssemblyDispla
144147
public Local()
145148
{
146149
bindingVersion = GetVersionStringFromAssemblyEmbedded("BrowserStackLocal");
147-
tunnel = new BrowserStackTunnel();
150+
userAgent = userAgent + "/" + bindingVersion;
151+
tunnel = new BrowserStackTunnel(userAgent);
148152
}
149-
public void start(List<KeyValuePair<string, string>> options)
153+
154+
private void DownloadVerifyAndRunBinary()
150155
{
151156
tunnel.basePathsIndex = -1;
157+
tunnel.addBinaryPath(customBinaryPath, accessKey, isFallbackEnabled, downloadFailureException);
158+
try
159+
{
160+
while (true)
161+
{
162+
bool except = false;
163+
try
164+
{
165+
tunnel.Run(accessKey, folder, customLogPath, "start");
166+
}
167+
catch (System.ComponentModel.Win32Exception)
168+
{
169+
except = true;
170+
}
171+
catch (Exception e)
172+
{
173+
except = true;
174+
Console.WriteLine(e.ToString());
175+
}
176+
if (except)
177+
{
178+
tunnel.fallbackPaths();
179+
}
180+
else
181+
{
182+
break;
183+
}
184+
}
185+
}
186+
catch (Exception err)
187+
{
188+
if (!isFallbackEnabled)
189+
{
190+
isFallbackEnabled = true;
191+
downloadFailureException = err;
192+
DownloadVerifyAndRunBinary();
193+
}
194+
else
195+
{
196+
throw err;
197+
}
198+
}
199+
}
200+
201+
public void start(List<KeyValuePair<string, string>> options)
202+
{
152203
foreach (KeyValuePair<string, string> pair in options)
153204
{
154205
string key = pair.Key;
@@ -174,33 +225,9 @@ public void start(List<KeyValuePair<string, string>> options)
174225

175226
argumentString += "-logFile \"" + customLogPath + "\" ";
176227
argumentString += "--source \"c-sharp:" + bindingVersion + "\" ";
177-
tunnel.addBinaryPath(customBinaryPath);
178228
tunnel.addBinaryArguments(argumentString);
179-
while (true)
180-
{
181-
bool except = false;
182-
try
183-
{
184-
tunnel.Run(accessKey, folder, customLogPath, "start");
185-
}
186-
catch (System.ComponentModel.Win32Exception)
187-
{
188-
except = true;
189-
}
190-
catch (Exception e)
191-
{
192-
except = true;
193-
Console.WriteLine(e.ToString());
194-
}
195-
if (except)
196-
{
197-
tunnel.fallbackPaths();
198-
}
199-
else
200-
{
201-
break;
202-
}
203-
}
229+
230+
DownloadVerifyAndRunBinary();
204231
}
205232

206233
public void stop()

0 commit comments

Comments
 (0)