@@ -3,6 +3,7 @@ var https = require('https'),
33 fs = require ( 'fs' ) ,
44 path = require ( 'path' ) ,
55 os = require ( 'os' ) ,
6+ util = require ( 'util' ) ,
67 childProcess = require ( 'child_process' ) ,
78 zlib = require ( 'zlib' ) ,
89 HttpsProxyAgent = require ( 'https-proxy-agent' ) ,
@@ -14,9 +15,55 @@ const packageName = 'browserstack-local-nodejs';
1415function LocalBinary ( ) {
1516 this . hostOS = process . platform ;
1617 this . is64bits = process . arch == 'x64' ;
18+ this . baseRetries = 9 ;
19+ this . sourceURL = null ;
20+ this . downloadErrorMessage = null ;
21+
22+ this . getSourceUrl = function ( conf , retries ) {
23+ /* Request for an endpoint to download the local binary from Rails no more than twice with 5 retries each */
24+ if ( ! [ 4 , 9 ] . includes ( retries ) && this . sourceURL != null ) {
25+ return this . sourceURL ;
26+ }
27+
28+ if ( process . env . BINARY_DOWNLOAD_SOURCE_URL !== undefined && process . env . BINARY_DOWNLOAD_FALLBACK_ENABLED == 'true' && this . parentRetries != 4 ) {
29+ /* This is triggered from Local.js if there's an error executing the downloaded binary */
30+ return process . env . BINARY_DOWNLOAD_SOURCE_URL ;
31+ }
32+
33+ let cmd , opts ;
34+ cmd = 'node' ;
35+ opts = [ path . join ( __dirname , 'fetchDownloadSourceUrl.js' ) , this . key ] ;
36+
37+ if ( retries == 4 || ( process . env . BINARY_DOWNLOAD_FALLBACK_ENABLED == 'true' && this . parentRetries == 4 ) ) {
38+ opts . push ( true , this . downloadErrorMessage || process . env . BINARY_DOWNLOAD_ERROR_MESSAGE ) ;
39+ } else {
40+ opts . push ( false , null ) ;
41+ }
42+
43+ if ( conf . proxyHost && conf . proxyPort ) {
44+ opts . push ( conf . proxyHost , conf . proxyPort ) ;
45+ if ( conf . useCaCertificate ) {
46+ opts . push ( conf . useCaCertificate ) ;
47+ }
48+ } else if ( conf . useCaCertificate ) {
49+ opts . push ( undefined , undefined , conf . useCaCertificate ) ;
50+ }
51+
52+ const userAgent = [ packageName , version ] . join ( '/' ) ;
53+ const env = Object . assign ( { 'USER_AGENT' : userAgent } , process . env ) ;
54+ const obj = childProcess . spawnSync ( cmd , opts , { env : env } ) ;
55+ if ( obj . stdout . length > 0 ) {
56+ this . sourceURL = obj . stdout . toString ( ) . replace ( / \n + $ / , '' ) ;
57+ process . env . BINARY_DOWNLOAD_SOURCE_URL = this . sourceURL ;
58+ return this . sourceURL ;
59+ } else if ( obj . stderr . length > 0 ) {
60+ let output = Buffer . from ( JSON . parse ( JSON . stringify ( obj . stderr ) ) . data ) . toString ( ) ;
61+ throw ( output ) ;
62+ }
63+ } ;
1764
18- this . getDownloadPath = function ( ) {
19- let sourceURL = 'https://www.browserstack.com/local-testing/downloads/binaries /';
65+ this . getDownloadPath = function ( conf , retries ) {
66+ let sourceURL = this . getSourceUrl ( conf , retries ) + ' /';
2067
2168 if ( this . hostOS . match ( / d a r w i n | m a c o s / i) ) {
2269 return sourceURL + 'BrowserStackLocal-darwin-x64' ;
@@ -43,9 +90,10 @@ function LocalBinary(){
4390 }
4491 } ;
4592
46- this . httpPath = this . getDownloadPath ( ) ;
47-
48-
93+ this . binaryDownloadError = function ( errorMessagePrefix , errorMessage ) {
94+ console . error ( errorMessagePrefix , errorMessage ) ;
95+ this . downloadErrorMessage = errorMessagePrefix + ' : ' + errorMessage ;
96+ } ;
4997
5098 this . retryBinaryDownload = function ( conf , destParentDir , callback , retries , binaryPath ) {
5199 var that = this ;
@@ -66,6 +114,12 @@ function LocalBinary(){
66114 } ;
67115
68116 this . downloadSync = function ( conf , destParentDir , retries ) {
117+ try {
118+ this . httpPath = this . getDownloadPath ( conf , retries ) ;
119+ } catch ( e ) {
120+ return console . error ( `Unable to fetch the source url to download the binary with error: ${ e } ` ) ;
121+ }
122+
69123 console . log ( 'Downloading in sync' ) ;
70124 var that = this ;
71125 if ( ! this . checkPath ( destParentDir ) )
@@ -96,21 +150,27 @@ function LocalBinary(){
96150 fs . chmodSync ( binaryPath , '0755' ) ;
97151 return binaryPath ;
98152 } else {
99- console . log ( 'failed to download' ) ;
153+ that . binaryDownloadError ( 'failed to download' ) ;
100154 return that . retryBinaryDownload ( conf , destParentDir , null , retries , binaryPath ) ;
101155 }
102156 } else if ( obj . stderr . length > 0 ) {
103157 output = Buffer . from ( JSON . parse ( JSON . stringify ( obj . stderr ) ) . data ) . toString ( ) ;
104- console . error ( output ) ;
158+ that . binaryDownloadError ( output ) ;
105159 return that . retryBinaryDownload ( conf , destParentDir , null , retries , binaryPath ) ;
106160 }
107161 } catch ( err ) {
108- console . error ( 'Download failed with error' , err ) ;
162+ that . binaryDownloadError ( 'Download failed with error' , util . format ( err ) ) ;
109163 return that . retryBinaryDownload ( conf , destParentDir , null , retries , binaryPath ) ;
110164 }
111165 } ;
112166
113167 this . download = function ( conf , destParentDir , callback , retries ) {
168+ try {
169+ this . httpPath = this . getDownloadPath ( conf , retries ) ;
170+ } catch ( e ) {
171+ return console . error ( `Unable to fetch the source url to download the binary with error: ${ e } ` ) ;
172+ }
173+
114174 var that = this ;
115175 if ( ! this . checkPath ( destParentDir ) )
116176 fs . mkdirSync ( destParentDir ) ;
@@ -152,11 +212,11 @@ function LocalBinary(){
152212 }
153213
154214 response . on ( 'error' , function ( err ) {
155- console . error ( 'Got Error in binary download response' , err ) ;
215+ that . binaryDownloadError ( 'Got Error in binary download response' , util . format ( err ) ) ;
156216 that . retryBinaryDownload ( conf , destParentDir , callback , retries , binaryPath ) ;
157217 } ) ;
158218 fileStream . on ( 'error' , function ( err ) {
159- console . error ( 'Got Error while downloading binary file' , err ) ;
219+ that . binaryDownloadError ( 'Got Error while downloading binary file' , util . format ( err ) ) ;
160220 that . retryBinaryDownload ( conf , destParentDir , callback , retries , binaryPath ) ;
161221 } ) ;
162222 fileStream . on ( 'close' , function ( ) {
@@ -165,12 +225,14 @@ function LocalBinary(){
165225 } ) ;
166226 } ) ;
167227 } ) . on ( 'error' , function ( err ) {
168- console . error ( 'Got Error in binary downloading request' , err ) ;
228+ that . binaryDownloadError ( 'Got Error in binary downloading request' , util . format ( err ) ) ;
169229 that . retryBinaryDownload ( conf , destParentDir , callback , retries , binaryPath ) ;
170230 } ) ;
171231 } ;
172232
173- this . binaryPath = function ( conf , callback ) {
233+ this . binaryPath = function ( conf , key , parentRetries , callback ) {
234+ this . key = key ;
235+ this . parentRetries = parentRetries ;
174236 var destParentDir = this . getAvailableDirs ( ) ;
175237 var destBinaryName = ( this . windows ) ? 'BrowserStackLocal.exe' : 'BrowserStackLocal' ;
176238 var binaryPath = path . join ( destParentDir , destBinaryName ) ;
@@ -180,10 +242,11 @@ function LocalBinary(){
180242 }
181243 callback ( binaryPath ) ;
182244 } else {
245+ let retries = this . baseRetries ;
183246 if ( ! callback ) {
184- return this . downloadSync ( conf , destParentDir , 5 ) ;
247+ return this . downloadSync ( conf , destParentDir , retries ) ;
185248 }
186- this . download ( conf , destParentDir , callback , 5 ) ;
249+ this . download ( conf , destParentDir , callback , retries ) ;
187250 }
188251 } ;
189252
0 commit comments