Refactor integrity check in FileDownloadTask, support multi algorithms

This commit is contained in:
yushijinhun
2018-06-30 16:44:37 +08:00
parent c4db9c9e12
commit 7ee7ef25f9
7 changed files with 101 additions and 20 deletions

View File

@@ -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))));
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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())
));
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}
}