Auto-Update improvements

NOTE: Downloaded update JARs are now expected to have been XORed with 0x5A!

This is to help prevent Windows Firewall from blocking update downloads
based on deep packet inspection.

Download read timeout reduced from 5s to 3s.

Download locations reordered so github entries are at the top as they have
better CDNs.

ApplyUpdate now assumes null response from GET /admin/stop means node
is not running.

ApplyUpdate now checks replacement JAR actually exists before attempting
to overwrite previous version.

ApplyUpdate now tries to use Windows EXE launcher in preference to raw
java command line. (This should improve Windows installer behaviour
in detecting running process and possibly firewall implications too).
This commit is contained in:
catbref 2019-06-17 09:10:06 +01:00
parent 2e6d33659c
commit 4cdb5e6b95
3 changed files with 23 additions and 3 deletions

View File

@ -32,6 +32,7 @@ public class ApplyUpdate {
private static final Logger LOGGER = LogManager.getLogger(ApplyUpdate.class);
private static final String JAR_FILENAME = AutoUpdate.JAR_FILENAME;
private static final String NEW_JAR_FILENAME = AutoUpdate.NEW_JAR_FILENAME;
private static final String WINDOWS_EXE_LAUNCHER = "qora-core.exe";
private static final long CHECK_INTERVAL = 5 * 1000; // ms
private static final int MAX_ATTEMPTS = 5;
@ -66,7 +67,7 @@ public class ApplyUpdate {
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
LOGGER.debug(String.format("Attempt #%d out of %d to shutdown node", attempt + 1, MAX_ATTEMPTS));
String response = ApiRequest.perform(BASE_URI + "admin/stop", null);
if (response != null)
if (response == null)
break;
try {
@ -90,6 +91,11 @@ public class ApplyUpdate {
Path realJar = Paths.get(JAR_FILENAME);
Path newJar = Paths.get(NEW_JAR_FILENAME);
if (!Files.exists(newJar)) {
LOGGER.warn(String.format("Replacement JAR '%s' not found?", newJar));
return;
}
int attempt;
for (attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
LOGGER.debug(String.format("Attempt #%d out of %d to replace JAR", attempt + 1, MAX_ATTEMPTS));
@ -118,8 +124,16 @@ public class ApplyUpdate {
Path javaBinary = Paths.get(javaHome, "bin", "java");
LOGGER.debug(String.format("Java binary: %s", javaBinary));
Path exeLauncher = Paths.get(WINDOWS_EXE_LAUNCHER);
LOGGER.debug(String.format("Windows EXE launcher: %s", exeLauncher));
List<String> javaCmd;
if (Files.exists(exeLauncher))
javaCmd = Arrays.asList(exeLauncher.toString());
else
javaCmd = Arrays.asList(javaBinary.toString(), "-jar", JAR_FILENAME);
try {
List<String> javaCmd = Arrays.asList(javaBinary.toString(), "-jar", JAR_FILENAME);
LOGGER.info(String.format("Restarting node with: %s", String.join(" ", javaCmd)));
new ProcessBuilder(javaCmd).start();

View File

@ -150,7 +150,7 @@ public class ApiRequest {
con.setRequestMethod("GET");
con.setConnectTimeout(5000);
con.setReadTimeout(5000);
con.setReadTimeout(3000);
ApiRequest.setConnectionSSL(con, ipAddress);
int status = con.getResponseCode();

View File

@ -49,6 +49,9 @@ public class AutoUpdate extends Thread {
private static final int GIT_COMMIT_HASH_LENGTH = 20; // SHA-1
private static final int EXPECTED_DATA_LENGTH = Transformer.TIMESTAMP_LENGTH + GIT_COMMIT_HASH_LENGTH + Transformer.SHA256_LENGTH;
/** This byte value used to hide contents from deep-inspection firewalls in case they block updates. */
private static final byte XOR_VALUE = (byte) 0x5a;
private static AutoUpdate instance;
private volatile boolean isStopping = false;
@ -170,6 +173,9 @@ public class AutoUpdate extends Thread {
if (nread == -1)
break;
for (int i = 0; i < nread; ++i)
buffer[i] ^= XOR_VALUE;
sha256.update(buffer, 0, nread);
out.write(buffer, 0, nread);
} while (true);