Skip to content

Change Binary Download Distribution #161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/Local.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,9 @@ function Local(){
conf.useCaCertificate = this.useCaCertificate;
}
if(!callback) {
return this.binary.binaryPath(conf);
return this.binary.binaryPath(conf, this.key);
}
this.binary.binaryPath(conf, callback);
this.binary.binaryPath(conf, this.key, callback);
} else {
console.log('BINARY PATH IS DEFINED');
if(!callback) {
Expand Down
78 changes: 64 additions & 14 deletions lib/LocalBinary.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,44 @@ const packageName = 'browserstack-local-nodejs';
function LocalBinary(){
this.hostOS = process.platform;
this.is64bits = process.arch == 'x64';
this.baseRetries = 10;
this.sourceURL = null;
this.downloadErrorMessage = null;

this.getSourceUrl = function(conf, retries) {
/* Request for an endpoint from Rails no more than twice with 5 retries each */
if (![5, 10].includes(retries) && this.sourceURL) return this.sourceURL;

let cmd, opts;
cmd = 'node';
opts = [path.join(__dirname, 'fetchDownloadSourceUrl.js'), this.key];
if(conf.proxyHost && conf.proxyPort) {
opts.push(conf.proxyHost, conf.proxyPort);
if (conf.useCaCertificate) {
opts.push(conf.useCaCertificate);
}
} else if (conf.useCaCertificate) {
opts.push(undefined, undefined, conf.useCaCertificate);
}

this.getDownloadPath = function () {
let sourceURL = 'https://www.browserstack.com/local-testing/downloads/binaries/';
if (retries == 5) {
opts.push(true, this.binaryDownloadError);
}

const userAgent = [packageName, version].join('/');
const env = Object.assign({ 'USER_AGENT': userAgent }, process.env);
const obj = childProcess.spawnSync(cmd, opts, { env: env });
if(obj.stdout.length > 0) {
this.sourceURL = obj.stdout;
return this.sourceURL;
} else if(obj.stderr.length > 0) {
let output = Buffer.from(JSON.parse(JSON.stringify(obj.stderr)).data).toString();
throw(output);
}
}

this.getDownloadPath = function (conf, retries) {
let sourceURL = this.getSourceUrl(conf, retries) + '/';

if(this.hostOS.match(/darwin|mac os/i)){
return sourceURL + 'BrowserStackLocal-darwin-x64';
Expand All @@ -43,9 +78,10 @@ function LocalBinary(){
}
};

this.httpPath = this.getDownloadPath();


this.binaryDownloadError = function(errorMessagePrefix, errorObject) {
console.error(errorMessagePrefix, errorObject);
this.downloadErrorMessage = errorMessagePrefix + " : " + errorObject ? errorObject.getMessage() : "null";
}

this.retryBinaryDownload = function(conf, destParentDir, callback, retries, binaryPath) {
var that = this;
Expand All @@ -66,6 +102,12 @@ function LocalBinary(){
};

this.downloadSync = function(conf, destParentDir, retries) {
try {
this.httpPath = this.getDownloadPath(conf, retries);
} catch (e) {
return console.error(`Unable to fetch the source url to download the binary with error: ${e}`);
}

console.log('Downloading in sync');
var that = this;
if(!this.checkPath(destParentDir))
Expand Down Expand Up @@ -96,21 +138,27 @@ function LocalBinary(){
fs.chmodSync(binaryPath, '0755');
return binaryPath;
}else{
console.log('failed to download');
that.binaryDownloadError('failed to download');
return that.retryBinaryDownload(conf, destParentDir, null, retries, binaryPath);
}
} else if(obj.stderr.length > 0) {
output = Buffer.from(JSON.parse(JSON.stringify(obj.stderr)).data).toString();
console.error(output);
that.binaryDownloadError(output);
return that.retryBinaryDownload(conf, destParentDir, null, retries, binaryPath);
}
} catch(err) {
console.error('Download failed with error', err);
that.binaryDownloadError('Download failed with error', err);
return that.retryBinaryDownload(conf, destParentDir, null, retries, binaryPath);
}
};

this.download = function(conf, destParentDir, callback, retries){
try {
this.httpPath = this.getDownloadPath(conf, retries);
} catch (e) {
return console.error(`Unable to fetch the source url to download the binary with error: ${e}`);
}

var that = this;
if(!this.checkPath(destParentDir))
fs.mkdirSync(destParentDir);
Expand Down Expand Up @@ -152,11 +200,11 @@ function LocalBinary(){
}

response.on('error', function(err) {
console.error('Got Error in binary download response', err);
that.binaryDownloadError('Got Error in binary download response', err);
that.retryBinaryDownload(conf, destParentDir, callback, retries, binaryPath);
});
fileStream.on('error', function (err) {
console.error('Got Error while downloading binary file', err);
that.binaryDownloadError('Got Error while downloading binary file', err);
that.retryBinaryDownload(conf, destParentDir, callback, retries, binaryPath);
});
fileStream.on('close', function () {
Expand All @@ -165,12 +213,13 @@ function LocalBinary(){
});
});
}).on('error', function(err) {
console.error('Got Error in binary downloading request', err);
that.binaryDownloadError('Got Error in binary downloading request', err);
that.retryBinaryDownload(conf, destParentDir, callback, retries, binaryPath);
});
};

this.binaryPath = function(conf, callback){
this.binaryPath = function(conf, key, callback){
this.key = key;
var destParentDir = this.getAvailableDirs();
var destBinaryName = (this.windows) ? 'BrowserStackLocal.exe' : 'BrowserStackLocal';
var binaryPath = path.join(destParentDir, destBinaryName);
Expand All @@ -180,10 +229,11 @@ function LocalBinary(){
}
callback(binaryPath);
} else {
let retries = this.baseRetries;
if(!callback) {
return this.downloadSync(conf, destParentDir, 5);
return this.downloadSync(conf, destParentDir, retries);
}
this.download(conf, destParentDir, callback, 5);
this.download(conf, destParentDir, callback, retries);
}
};

Expand Down
57 changes: 57 additions & 0 deletions lib/fetchDownloadSourceUrl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
const https = require('https'),
fs = require('fs'),
HttpsProxyAgent = require('https-proxy-agent');

const authToken = process.argv[2], proxyHost = process.argv[3], proxyPort = process.argv[4], useCaCertificate = process.argv[5], downloadFallback = process.argv[6], downloadErrorMessage = process.argv[7];

let body = '', data = {"auth_token": authToken};
const options = {
hostname: 'local.browserstack.com',
port: 443,
path: '/binary/api/v1/endpoint',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'user-agent': process.env.USER_AGENT
}
};
if (downloadFallback) {
options.headers['X-Local-Fallback-Cloudflare'] = true;
data["error_message"] = downloadErrorMessage;
}

if(proxyHost && proxyPort) {
options.agent = new HttpsProxyAgent({
host: proxyHost,
port: proxyPort
});
}
if (useCaCertificate) {
try {
options.ca = fs.readFileSync(useCaCertificate);
} catch(err) {
console.log('failed to read cert file', err);
}
}

const req = https.request(options, res => {
res.on('data', d => {
body += d;
});
res.on('end', () => {
try {
const url = JSON.parse(body).data.endpoint;
console.log(url);
} catch (e) {
console.error(e);
}
});
res.on('error', (err) => {
console.error(err);
})
});
req.on('error', e => {
console.error(e);
});
req.write(JSON.stringify(data));
req.end();
6 changes: 3 additions & 3 deletions test/local.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ describe('LocalBinary', function () {
// ensure that we have a valid binary downloaded

// removeIfInvalid();
(new LocalBinary()).binaryPath({}, function(binaryPath) {
(new LocalBinary()).binaryPath({}, 'abc', function(binaryPath) {
defaultBinaryPath = binaryPath;
tempfs.mkdir({
recursive: true
Expand Down Expand Up @@ -313,7 +313,7 @@ describe('LocalBinary', function () {
fs.writeFile(defaultBinaryPath, 'Random String', function() {
fs.chmod(defaultBinaryPath, '0755', function() {
localBinary.binaryPath({
}, function(binaryPath) {
}, 'abc', function(binaryPath) {
expect(downloadStub.called).to.be.true;
done();
});
Expand All @@ -331,7 +331,7 @@ describe('LocalBinary', function () {
});

localBinary.binaryPath({
}, function(binaryPath) {
}, 'abc', function(binaryPath) {
expect(downloadStub.called).to.be.true;
done();
});
Expand Down