Refactor integrity check in FileDownloadTask, support multi algorithms
This commit is contained in:
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.game;
|
||||
import org.jackhuang.hmcl.setting.Profile;
|
||||
import org.jackhuang.hmcl.setting.Settings;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
@@ -70,7 +71,7 @@ public class HMCLGameDownloadTask extends Task {
|
||||
NetworkUtils.toURL(profile.getDependency().getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
||||
cache,
|
||||
profile.getDependency().getProxy(),
|
||||
version.getDownloadInfo().getSha1()
|
||||
new IntegrityCheck("SHA-1", version.getDownloadInfo().getSha1())
|
||||
).then(Task.of(v -> FileUtils.copyFile(cache, jar))));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.jfoenix.concurrency.JFXUtilities;
|
||||
import javafx.scene.layout.Region;
|
||||
import org.jackhuang.hmcl.Launcher;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||
import org.jackhuang.hmcl.ui.Controllers;
|
||||
@@ -182,7 +183,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
|
||||
@Override
|
||||
public Collection<Task> getDependents() {
|
||||
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash));
|
||||
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, new IntegrityCheck("SHA-1", hash)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -232,7 +233,7 @@ public class AppDataUpgrader extends IUpgrader {
|
||||
|
||||
@Override
|
||||
public Collection<Task> getDependents() {
|
||||
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash));
|
||||
return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, new IntegrityCheck("SHA-1", hash)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
||||
import org.jackhuang.hmcl.game.AssetObject;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
@@ -106,7 +107,7 @@ public final class GameAssetDownloadTask extends Task {
|
||||
flag = !file.exists();
|
||||
}
|
||||
if (flag) {
|
||||
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), assetObject.getHash());
|
||||
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), new IntegrityCheck("SHA-1", assetObject.getHash()));
|
||||
task.setName(assetObject.getHash());
|
||||
dependencies.add(task);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.download.game;
|
||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||
import org.jackhuang.hmcl.game.Version;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||
|
||||
@@ -56,7 +57,7 @@ public final class GameDownloadTask extends Task {
|
||||
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
||||
jar,
|
||||
dependencyManager.getProxy(),
|
||||
version.getDownloadInfo().getSha1()
|
||||
new IntegrityCheck("SHA-1", version.getDownloadInfo().getSha1())
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||
import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
||||
import org.jackhuang.hmcl.game.Library;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||
import org.jackhuang.hmcl.task.Task;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.IOUtils;
|
||||
@@ -48,7 +49,7 @@ public final class LibraryDownloadTask extends Task {
|
||||
setSignificance(TaskSignificance.MODERATE);
|
||||
|
||||
task = new FileDownloadTask(NetworkUtils.toURL(url),
|
||||
file, dependencyManager.getProxy(), library.getDownload().getSha1());
|
||||
file, dependencyManager.getProxy(), new IntegrityCheck("SHA-1", library.getDownload().getSha1()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.task;
|
||||
|
||||
import org.jackhuang.hmcl.event.EventManager;
|
||||
import org.jackhuang.hmcl.event.FailedEvent;
|
||||
import org.jackhuang.hmcl.util.ChecksumMismatchException;
|
||||
import org.jackhuang.hmcl.util.FileUtils;
|
||||
import org.jackhuang.hmcl.util.IOUtils;
|
||||
import org.jackhuang.hmcl.util.Logging;
|
||||
@@ -35,6 +36,7 @@ import java.net.URL;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static org.jackhuang.hmcl.util.DigestUtils.getDigest;
|
||||
|
||||
/**
|
||||
@@ -44,9 +46,38 @@ import static org.jackhuang.hmcl.util.DigestUtils.getDigest;
|
||||
*/
|
||||
public class FileDownloadTask extends Task {
|
||||
|
||||
public static class IntegrityCheck {
|
||||
private String algorithm;
|
||||
private String checksum;
|
||||
|
||||
public IntegrityCheck(String algorithm, String checksum) {
|
||||
this.algorithm = requireNonNull(algorithm);
|
||||
this.checksum = requireNonNull(checksum);
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public String getChecksum() {
|
||||
return checksum;
|
||||
}
|
||||
|
||||
public MessageDigest createDigest() {
|
||||
return getDigest(algorithm);
|
||||
}
|
||||
|
||||
public void performCheck(MessageDigest digest) throws ChecksumMismatchException {
|
||||
String actualChecksum = String.format("%1$040x", new BigInteger(1, digest.digest()));
|
||||
if (!checksum.equalsIgnoreCase(actualChecksum)) {
|
||||
throw new ChecksumMismatchException(algorithm, checksum, actualChecksum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final URL url;
|
||||
private final File file;
|
||||
private final String hash;
|
||||
private final IntegrityCheck integrityCheck;
|
||||
private final int retry;
|
||||
private final Proxy proxy;
|
||||
private final EventManager<FailedEvent<URL>> onFailed = new EventManager<>();
|
||||
@@ -74,23 +105,23 @@ public class FileDownloadTask extends Task {
|
||||
* @param url the URL of remote file.
|
||||
* @param file the location that download to.
|
||||
* @param proxy the proxy.
|
||||
* @param hash the SHA-1 hash code of remote file, null if the hash is unknown or it is no need to check the hash code.
|
||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||
*/
|
||||
public FileDownloadTask(URL url, File file, Proxy proxy, String hash) {
|
||||
this(url, file, proxy, hash, 5);
|
||||
public FileDownloadTask(URL url, File file, Proxy proxy, IntegrityCheck integrityCheck) {
|
||||
this(url, file, proxy, integrityCheck, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url the URL of remote file.
|
||||
* @param file the location that download to.
|
||||
* @param hash the SHA-1 hash code of remote file, null if the hash is unknown or it is no need to check the hash code.
|
||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||
* @param retry the times for retrying if downloading fails.
|
||||
* @param proxy the proxy.
|
||||
*/
|
||||
public FileDownloadTask(URL url, File file, Proxy proxy, String hash, int retry) {
|
||||
public FileDownloadTask(URL url, File file, Proxy proxy, IntegrityCheck integrityCheck, int retry) {
|
||||
this.url = url;
|
||||
this.file = file;
|
||||
this.hash = hash;
|
||||
this.integrityCheck = integrityCheck;
|
||||
this.retry = retry;
|
||||
this.proxy = proxy;
|
||||
|
||||
@@ -159,7 +190,7 @@ public class FileDownloadTask extends Task {
|
||||
temp = FileUtils.createTempFile();
|
||||
rFile = new RandomAccessFile(temp, "rw");
|
||||
|
||||
MessageDigest digest = getDigest("SHA-1");
|
||||
MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest();
|
||||
|
||||
stream = con.getInputStream();
|
||||
int lastDownloaded = 0, downloaded = 0;
|
||||
@@ -175,8 +206,9 @@ public class FileDownloadTask extends Task {
|
||||
if (read == -1)
|
||||
break;
|
||||
|
||||
if (hash != null)
|
||||
if (digest != null) {
|
||||
digest.update(buffer, 0, read);
|
||||
}
|
||||
|
||||
// Write buffer to file.
|
||||
rFile.write(buffer, 0, read);
|
||||
@@ -214,11 +246,9 @@ public class FileDownloadTask extends Task {
|
||||
if (downloaded != contentLength)
|
||||
throw new IllegalStateException("Unexpected file size: " + downloaded + ", expected: " + contentLength);
|
||||
|
||||
// Check hash code
|
||||
if (hash != null) {
|
||||
String hashCode = String.format("%1$040x", new BigInteger(1, digest.digest()));
|
||||
if (!hash.equalsIgnoreCase(hashCode))
|
||||
throw new IllegalStateException("Unexpected hash code: " + hashCode + ", expected: " + hash);
|
||||
// Integrity check
|
||||
if (integrityCheck != null) {
|
||||
integrityCheck.performCheck(digest);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher.
|
||||
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ChecksumMismatchException extends IOException {
|
||||
|
||||
private String algorithm;
|
||||
private String expectedChecksum;
|
||||
private String actualChecksum;
|
||||
|
||||
public ChecksumMismatchException(String algorithm, String expectedChecksum, String actualChecksum) {
|
||||
super("Incorrect checksum (" + algorithm + "), expected: " + expectedChecksum + ", actual: " + actualChecksum);
|
||||
this.algorithm = algorithm;
|
||||
this.expectedChecksum = expectedChecksum;
|
||||
this.actualChecksum = actualChecksum;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
public String getExpectedChecksum() {
|
||||
return expectedChecksum;
|
||||
}
|
||||
|
||||
public String getActualChecksum() {
|
||||
return actualChecksum;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user