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.Profile;
|
||||||
import org.jackhuang.hmcl.setting.Settings;
|
import org.jackhuang.hmcl.setting.Settings;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.FileUtils;
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
@@ -70,7 +71,7 @@ public class HMCLGameDownloadTask extends Task {
|
|||||||
NetworkUtils.toURL(profile.getDependency().getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
NetworkUtils.toURL(profile.getDependency().getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
||||||
cache,
|
cache,
|
||||||
profile.getDependency().getProxy(),
|
profile.getDependency().getProxy(),
|
||||||
version.getDownloadInfo().getSha1()
|
new IntegrityCheck("SHA-1", version.getDownloadInfo().getSha1())
|
||||||
).then(Task.of(v -> FileUtils.copyFile(cache, jar))));
|
).then(Task.of(v -> FileUtils.copyFile(cache, jar))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.jfoenix.concurrency.JFXUtilities;
|
|||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import org.jackhuang.hmcl.Launcher;
|
import org.jackhuang.hmcl.Launcher;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
import org.jackhuang.hmcl.task.TaskExecutor;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
@@ -182,7 +183,7 @@ public class AppDataUpgrader extends IUpgrader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Task> getDependents() {
|
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
|
@Override
|
||||||
@@ -232,7 +233,7 @@ public class AppDataUpgrader extends IUpgrader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Task> getDependents() {
|
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
|
@Override
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.AbstractDependencyManager;
|
|||||||
import org.jackhuang.hmcl.game.AssetObject;
|
import org.jackhuang.hmcl.game.AssetObject;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.FileUtils;
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
@@ -106,7 +107,7 @@ public final class GameAssetDownloadTask extends Task {
|
|||||||
flag = !file.exists();
|
flag = !file.exists();
|
||||||
}
|
}
|
||||||
if (flag) {
|
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());
|
task.setName(assetObject.getHash());
|
||||||
dependencies.add(task);
|
dependencies.add(task);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.download.game;
|
|||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.NetworkUtils;
|
import org.jackhuang.hmcl.util.NetworkUtils;
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ public final class GameDownloadTask extends Task {
|
|||||||
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())),
|
||||||
jar,
|
jar,
|
||||||
dependencyManager.getProxy(),
|
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.download.AbstractDependencyManager;
|
||||||
import org.jackhuang.hmcl.game.Library;
|
import org.jackhuang.hmcl.game.Library;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
|
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.FileUtils;
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.IOUtils;
|
import org.jackhuang.hmcl.util.IOUtils;
|
||||||
@@ -48,7 +49,7 @@ public final class LibraryDownloadTask extends Task {
|
|||||||
setSignificance(TaskSignificance.MODERATE);
|
setSignificance(TaskSignificance.MODERATE);
|
||||||
|
|
||||||
task = new FileDownloadTask(NetworkUtils.toURL(url),
|
task = new FileDownloadTask(NetworkUtils.toURL(url),
|
||||||
file, dependencyManager.getProxy(), library.getDownload().getSha1());
|
file, dependencyManager.getProxy(), new IntegrityCheck("SHA-1", library.getDownload().getSha1()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.task;
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.event.EventManager;
|
import org.jackhuang.hmcl.event.EventManager;
|
||||||
import org.jackhuang.hmcl.event.FailedEvent;
|
import org.jackhuang.hmcl.event.FailedEvent;
|
||||||
|
import org.jackhuang.hmcl.util.ChecksumMismatchException;
|
||||||
import org.jackhuang.hmcl.util.FileUtils;
|
import org.jackhuang.hmcl.util.FileUtils;
|
||||||
import org.jackhuang.hmcl.util.IOUtils;
|
import org.jackhuang.hmcl.util.IOUtils;
|
||||||
import org.jackhuang.hmcl.util.Logging;
|
import org.jackhuang.hmcl.util.Logging;
|
||||||
@@ -35,6 +36,7 @@ import java.net.URL;
|
|||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
import static org.jackhuang.hmcl.util.DigestUtils.getDigest;
|
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 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 URL url;
|
||||||
private final File file;
|
private final File file;
|
||||||
private final String hash;
|
private final IntegrityCheck integrityCheck;
|
||||||
private final int retry;
|
private final int retry;
|
||||||
private final Proxy proxy;
|
private final Proxy proxy;
|
||||||
private final EventManager<FailedEvent<URL>> onFailed = new EventManager<>();
|
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 url the URL of remote file.
|
||||||
* @param file the location that download to.
|
* @param file the location that download to.
|
||||||
* @param proxy the proxy.
|
* @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) {
|
public FileDownloadTask(URL url, File file, Proxy proxy, IntegrityCheck integrityCheck) {
|
||||||
this(url, file, proxy, hash, 5);
|
this(url, file, proxy, integrityCheck, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param url the URL of remote file.
|
* @param url the URL of remote file.
|
||||||
* @param file the location that download to.
|
* @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 retry the times for retrying if downloading fails.
|
||||||
* @param proxy the proxy.
|
* @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.url = url;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.hash = hash;
|
this.integrityCheck = integrityCheck;
|
||||||
this.retry = retry;
|
this.retry = retry;
|
||||||
this.proxy = proxy;
|
this.proxy = proxy;
|
||||||
|
|
||||||
@@ -159,7 +190,7 @@ public class FileDownloadTask extends Task {
|
|||||||
temp = FileUtils.createTempFile();
|
temp = FileUtils.createTempFile();
|
||||||
rFile = new RandomAccessFile(temp, "rw");
|
rFile = new RandomAccessFile(temp, "rw");
|
||||||
|
|
||||||
MessageDigest digest = getDigest("SHA-1");
|
MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest();
|
||||||
|
|
||||||
stream = con.getInputStream();
|
stream = con.getInputStream();
|
||||||
int lastDownloaded = 0, downloaded = 0;
|
int lastDownloaded = 0, downloaded = 0;
|
||||||
@@ -175,8 +206,9 @@ public class FileDownloadTask extends Task {
|
|||||||
if (read == -1)
|
if (read == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (hash != null)
|
if (digest != null) {
|
||||||
digest.update(buffer, 0, read);
|
digest.update(buffer, 0, read);
|
||||||
|
}
|
||||||
|
|
||||||
// Write buffer to file.
|
// Write buffer to file.
|
||||||
rFile.write(buffer, 0, read);
|
rFile.write(buffer, 0, read);
|
||||||
@@ -214,11 +246,9 @@ public class FileDownloadTask extends Task {
|
|||||||
if (downloaded != contentLength)
|
if (downloaded != contentLength)
|
||||||
throw new IllegalStateException("Unexpected file size: " + downloaded + ", expected: " + contentLength);
|
throw new IllegalStateException("Unexpected file size: " + downloaded + ", expected: " + contentLength);
|
||||||
|
|
||||||
// Check hash code
|
// Integrity check
|
||||||
if (hash != null) {
|
if (integrityCheck != null) {
|
||||||
String hashCode = String.format("%1$040x", new BigInteger(1, digest.digest()));
|
integrityCheck.performCheck(digest);
|
||||||
if (!hash.equalsIgnoreCase(hashCode))
|
|
||||||
throw new IllegalStateException("Unexpected hash code: " + hashCode + ", expected: " + hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
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