Harden downloader against partial downloads by creating a lock file while downloading.

This commit is contained in:
modmuss50
2022-07-30 00:02:14 +01:00
parent 7f1b006d66
commit b7f4939632

View File

@@ -173,7 +173,10 @@ public class Download {
.map(this::getETagRequest)
.orElseGet(this::getRequest);
// Create a .lock file, this allows us to re-download if the download was forcefully aborted part way through.
createLock(output);
HttpResponse<Path> response = send(httpRequest, HttpResponse.BodyHandlers.ofFile(output));
getAndResetLock(output);
final int statusCode = response.statusCode();
boolean success = statusCode == HttpURLConnection.HTTP_NOT_MODIFIED || (statusCode >= 200 && statusCode < 300);
@@ -184,6 +187,12 @@ public class Download {
}
if (!success) {
try {
Files.deleteIfExists(output);
} catch (IOException ignored) {
// We tried.
}
throw error("HTTP request to (%s) returned unsuccessful status (%d)", url, statusCode);
}
@@ -203,6 +212,7 @@ public class Download {
try {
downloadedHash = Checksum.sha1Hex(output);
Files.deleteIfExists(output);
} catch (IOException e) {
downloadedHash = "unknown hash";
}
@@ -226,6 +236,11 @@ public class Download {
return false;
}
if (getAndResetLock(output)) {
LOGGER.warn("Forcing downloading {} as existing lock file was found. This may happen if the gradle build was forcefully canceled.", output);
return true;
}
if (expectedHash != null) {
final String hashAttribute = readHash(output).orElse("");
@@ -357,6 +372,33 @@ public class Download {
return basicView.readAttributes().lastModifiedTime();
}
private Path getLockFile(Path output) {
return output.resolveSibling(output.getFileName() + ".lock");
}
private boolean getAndResetLock(Path output) throws DownloadException {
final Path lock = getLockFile(output);
final boolean exists = Files.exists(lock);
try {
Files.deleteIfExists(lock);
} catch (IOException e) {
throw error(e, "Failed to release lock on %s", lock);
}
return exists;
}
private void createLock(Path output) throws DownloadException {
final Path lock = getLockFile(output);
try {
Files.createFile(lock);
} catch (IOException e) {
throw error(e, "Failed to acquire lock on %s", lock);
}
}
private DownloadException error(String message, Object... args) {
return new DownloadException(String.format(Locale.ENGLISH, message, args));
}