88using System . Security . Principal ;
99using 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+
1117namespace 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 ) )
0 commit comments