Skip to content

Commit

Permalink
Cachable, proxy-friendly selendroid jar download.
Browse files Browse the repository at this point in the history
* Download using http from maven central, not https from github. Using
  http avoids many proxy-related problems and makes caching
  easier. Not using github means can use http and also avoids a
  redirection to a temporary and therefore extra-hard-to-cache URL.

* Download via temporary file, throwing exception if fail, to ensure
  other code can't see bad download.

* Use SHA256 not MD5 fingerprint, to compensate for use of http.

* Only check fingerprint on download, but fail if it is wrong instead
  of just logging warning.

* Use Buffer, not String to hold body.

Download using http (from maven central, not github) via temporary file, throwing exception if fail.
  • Loading branch information
JeremyGreen-TomTom committed Sep 9, 2016
1 parent 6a8aac2 commit 3325a8b
Showing 1 changed file with 26 additions and 15 deletions.
41 changes: 26 additions & 15 deletions lib/installer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import path from 'path';
import { fs } from 'appium-support';
import { exec } from 'teen_process';
import log from './logger';
import crypto from 'crypto';

const SE_VER = "0.17.0";
const SE_DOWNLOAD = `https://github.com/selendroid/selendroid/releases/` +
`download/${SE_VER}/selendroid-standalone-${SE_VER}-with` +
`-dependencies.jar`;
const SE_DOWNLOAD_MD5 = "5e1f7de5e4d2eb77b68675d76c5edf6a";
const SE_DOWNLOAD = `http://repo1.maven.org/maven2/io/selendroid/selendroid-standalone/${SE_VER}/selendroid-standalone-${SE_VER}-with-dependencies.jar`;
const SE_DOWNLOAD_SHA256 = "7cf7163ac47f1c46eff95b62f78b58c1dabdec534acc6632da3784739f6e9d82";
const SE_DIR = path.resolve(__dirname, "..", "..", "selendroid");
const SE_DOWNLOAD_DIR = path.resolve(SE_DIR, "download");
const SE_JAR_PATH = path.resolve(SE_DOWNLOAD_DIR, "selendroid-server.jar");
// Use of temporary file means that SE_JAR_PATH can only exist if it has
// verified content.
const SE_JAR_PATH_TMP = path.resolve(SE_DOWNLOAD_DIR, "selendroid-server.jar.tmp");
// Putting fingerprint in file name means download triggered if fingerprint changed.
const SE_JAR_PATH = path.resolve(SE_DOWNLOAD_DIR, `selendroid-server-${SE_DOWNLOAD_SHA256}.jar`);
const SE_APK_PATH = path.resolve(SE_DIR, "selendroid-server.apk");
const SE_MANIFEST_PATH = path.resolve(SE_DIR, "AndroidManifest.xml");

Expand All @@ -26,9 +29,8 @@ async function setupSelendroid () {
return;
}
}
if (await fs.exists(SE_JAR_PATH) &&
await fs.md5(SE_JAR_PATH) === SE_DOWNLOAD_MD5) {
log.info("Standalone jar exists and has correct hash, skipping download");
if (await fs.exists(SE_JAR_PATH)) {
log.info("Standalone jar exists, skipping download: " + SE_JAR_PATH);
} else {
await downloadSelendroid();
}
Expand Down Expand Up @@ -63,18 +65,27 @@ async function downloadSelendroid () {
await fs.mkdir(SE_DOWNLOAD_DIR);
log.info(`Downloading Selendroid standalone server version ${SE_VER} from ` +
`${SE_DOWNLOAD} --> ${SE_JAR_PATH}`);
let body = await request.get({url: SE_DOWNLOAD, encoding: 'binary'});
log.info(`Writing binary content to ${SE_JAR_PATH}`);
await fs.writeFile(SE_JAR_PATH, body, {encoding: 'binary'});
await fs.chmod(SE_JAR_PATH, 0o0644);
if (await fs.md5(SE_JAR_PATH) === SE_DOWNLOAD_MD5) {
let body = await request.get({url: SE_DOWNLOAD, encoding: null});
if (!body instanceof Buffer) {
throw new Error(Object.prototype.toString.call(body));
}
log.info(`Writing binary content to ${SE_JAR_PATH_TMP}`);
await fs.writeFile(SE_JAR_PATH_TMP, body);
await fs.chmod(SE_JAR_PATH_TMP, 0o0644);
let fingerprint = await sha256(body);
if (fingerprint === SE_DOWNLOAD_SHA256) {
await fs.rename(SE_JAR_PATH_TMP, SE_JAR_PATH);
log.info("Selendroid standalone server downloaded");
} else {
log.warn("Selendroid standalone server downloaded, but MD5 hash did not " +
"match, please be careful");
log.errorAndThrow("bad SHA256 fingerprint: " + fingerprint + " bytes: " + body.length);
}
}

async function sha256 (buffer) {
const hash = crypto.createHash('sha256');
return hash.update(buffer).digest('hex');
}

async function getFilePathFromJar (fileRegex, jarPath) {
let {stdout} = await exec('jar', ['tf', jarPath]);
for (let line of stdout.split("\n")) {
Expand Down

0 comments on commit 3325a8b

Please sign in to comment.