314 lines
8.1 KiB
Java
314 lines
8.1 KiB
Java
package net.minecraft.util;
|
|
|
|
import com.google.common.hash.Funnels;
|
|
import com.google.common.hash.HashCode;
|
|
import com.google.common.hash.HashFunction;
|
|
import com.google.common.hash.Hasher;
|
|
import com.mojang.logging.LogUtils;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.UncheckedIOException;
|
|
import java.net.HttpURLConnection;
|
|
import java.net.Proxy;
|
|
import java.net.ServerSocket;
|
|
import java.net.URL;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.file.Files;
|
|
import java.nio.file.LinkOption;
|
|
import java.nio.file.Path;
|
|
import java.nio.file.StandardCopyOption;
|
|
import java.nio.file.StandardOpenOption;
|
|
import java.nio.file.attribute.FileTime;
|
|
import java.time.Instant;
|
|
import java.util.Map;
|
|
import java.util.OptionalLong;
|
|
import net.minecraft.FileUtil;
|
|
import org.apache.commons.io.IOUtils;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class HttpUtil {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
|
|
private HttpUtil() {
|
|
}
|
|
|
|
public static Path downloadFile(
|
|
Path saveFile,
|
|
URL url,
|
|
Map<String, String> requestProperties,
|
|
HashFunction hashFunction,
|
|
@Nullable HashCode hash,
|
|
int maxSize,
|
|
Proxy proxy,
|
|
HttpUtil.DownloadProgressListener progressListener
|
|
) {
|
|
HttpURLConnection httpURLConnection = null;
|
|
InputStream inputStream = null;
|
|
progressListener.requestStart();
|
|
Path path;
|
|
if (hash != null) {
|
|
path = cachedFilePath(saveFile, hash);
|
|
|
|
try {
|
|
if (checkExistingFile(path, hashFunction, hash)) {
|
|
LOGGER.info("Returning cached file since actual hash matches requested");
|
|
progressListener.requestFinished(true);
|
|
updateModificationTime(path);
|
|
return path;
|
|
}
|
|
} catch (IOException var35) {
|
|
LOGGER.warn("Failed to check cached file {}", path, var35);
|
|
}
|
|
|
|
try {
|
|
LOGGER.warn("Existing file {} not found or had mismatched hash", path);
|
|
Files.deleteIfExists(path);
|
|
} catch (IOException var34) {
|
|
progressListener.requestFinished(false);
|
|
throw new UncheckedIOException("Failed to remove existing file " + path, var34);
|
|
}
|
|
} else {
|
|
path = null;
|
|
}
|
|
|
|
Path hashCode2;
|
|
try {
|
|
httpURLConnection = (HttpURLConnection)url.openConnection(proxy);
|
|
httpURLConnection.setInstanceFollowRedirects(true);
|
|
requestProperties.forEach(httpURLConnection::setRequestProperty);
|
|
inputStream = httpURLConnection.getInputStream();
|
|
long l = httpURLConnection.getContentLengthLong();
|
|
OptionalLong optionalLong = l != -1L ? OptionalLong.of(l) : OptionalLong.empty();
|
|
FileUtil.createDirectoriesSafe(saveFile);
|
|
progressListener.downloadStart(optionalLong);
|
|
if (optionalLong.isPresent() && optionalLong.getAsLong() > maxSize) {
|
|
throw new IOException("Filesize is bigger than maximum allowed (file is " + optionalLong + ", limit is " + maxSize + ")");
|
|
}
|
|
|
|
if (path == null) {
|
|
Path path2 = Files.createTempFile(saveFile, "download", ".tmp");
|
|
|
|
try {
|
|
HashCode hashCode2x = downloadAndHash(hashFunction, maxSize, progressListener, inputStream, path2);
|
|
Path path3 = cachedFilePath(saveFile, hashCode2x);
|
|
if (!checkExistingFile(path3, hashFunction, hashCode2x)) {
|
|
Files.move(path2, path3, StandardCopyOption.REPLACE_EXISTING);
|
|
} else {
|
|
updateModificationTime(path3);
|
|
}
|
|
|
|
progressListener.requestFinished(true);
|
|
return path3;
|
|
} finally {
|
|
Files.deleteIfExists(path2);
|
|
}
|
|
}
|
|
|
|
HashCode hashCode = downloadAndHash(hashFunction, maxSize, progressListener, inputStream, path);
|
|
if (!hashCode.equals(hash)) {
|
|
throw new IOException("Hash of downloaded file (" + hashCode + ") did not match requested (" + hash + ")");
|
|
}
|
|
|
|
progressListener.requestFinished(true);
|
|
hashCode2 = path;
|
|
} catch (Throwable var36) {
|
|
if (httpURLConnection != null) {
|
|
InputStream inputStream2 = httpURLConnection.getErrorStream();
|
|
if (inputStream2 != null) {
|
|
try {
|
|
LOGGER.error("HTTP response error: {}", IOUtils.toString(inputStream2, StandardCharsets.UTF_8));
|
|
} catch (Exception var32) {
|
|
LOGGER.error("Failed to read response from server");
|
|
}
|
|
}
|
|
}
|
|
|
|
progressListener.requestFinished(false);
|
|
throw new IllegalStateException("Failed to download file " + url, var36);
|
|
} finally {
|
|
IOUtils.closeQuietly(inputStream);
|
|
}
|
|
|
|
return hashCode2;
|
|
}
|
|
|
|
private static void updateModificationTime(Path path) {
|
|
try {
|
|
Files.setLastModifiedTime(path, FileTime.from(Instant.now()));
|
|
} catch (IOException var2) {
|
|
LOGGER.warn("Failed to update modification time of {}", path, var2);
|
|
}
|
|
}
|
|
|
|
private static HashCode hashFile(Path path, HashFunction hashFunction) throws IOException {
|
|
Hasher hasher = hashFunction.newHasher();
|
|
OutputStream outputStream = Funnels.asOutputStream(hasher);
|
|
|
|
try {
|
|
InputStream inputStream = Files.newInputStream(path);
|
|
|
|
try {
|
|
inputStream.transferTo(outputStream);
|
|
} catch (Throwable var9) {
|
|
if (inputStream != null) {
|
|
try {
|
|
inputStream.close();
|
|
} catch (Throwable var8) {
|
|
var9.addSuppressed(var8);
|
|
}
|
|
}
|
|
|
|
throw var9;
|
|
}
|
|
|
|
if (inputStream != null) {
|
|
inputStream.close();
|
|
}
|
|
} catch (Throwable var10) {
|
|
if (outputStream != null) {
|
|
try {
|
|
outputStream.close();
|
|
} catch (Throwable var7) {
|
|
var10.addSuppressed(var7);
|
|
}
|
|
}
|
|
|
|
throw var10;
|
|
}
|
|
|
|
if (outputStream != null) {
|
|
outputStream.close();
|
|
}
|
|
|
|
return hasher.hash();
|
|
}
|
|
|
|
private static boolean checkExistingFile(Path path, HashFunction hashFunction, HashCode expectedHash) throws IOException {
|
|
if (Files.exists(path, new LinkOption[0])) {
|
|
HashCode hashCode = hashFile(path, hashFunction);
|
|
if (hashCode.equals(expectedHash)) {
|
|
return true;
|
|
}
|
|
|
|
LOGGER.warn("Mismatched hash of file {}, expected {} but found {}", path, expectedHash, hashCode);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private static Path cachedFilePath(Path path, HashCode hash) {
|
|
return path.resolve(hash.toString());
|
|
}
|
|
|
|
private static HashCode downloadAndHash(
|
|
HashFunction hashFuntion, int maxSize, HttpUtil.DownloadProgressListener progressListener, InputStream stream, Path outputPath
|
|
) throws IOException {
|
|
OutputStream outputStream = Files.newOutputStream(outputPath, StandardOpenOption.CREATE);
|
|
|
|
HashCode var11;
|
|
try {
|
|
Hasher hasher = hashFuntion.newHasher();
|
|
byte[] bs = new byte[8196];
|
|
long l = 0L;
|
|
|
|
int i;
|
|
while ((i = stream.read(bs)) >= 0) {
|
|
l += i;
|
|
progressListener.downloadedBytes(l);
|
|
if (l > maxSize) {
|
|
throw new IOException("Filesize was bigger than maximum allowed (got >= " + l + ", limit was " + maxSize + ")");
|
|
}
|
|
|
|
if (Thread.interrupted()) {
|
|
LOGGER.error("INTERRUPTED");
|
|
throw new IOException("Download interrupted");
|
|
}
|
|
|
|
outputStream.write(bs, 0, i);
|
|
hasher.putBytes(bs, 0, i);
|
|
}
|
|
|
|
var11 = hasher.hash();
|
|
} catch (Throwable var13) {
|
|
if (outputStream != null) {
|
|
try {
|
|
outputStream.close();
|
|
} catch (Throwable var12) {
|
|
var13.addSuppressed(var12);
|
|
}
|
|
}
|
|
|
|
throw var13;
|
|
}
|
|
|
|
if (outputStream != null) {
|
|
outputStream.close();
|
|
}
|
|
|
|
return var11;
|
|
}
|
|
|
|
public static int getAvailablePort() {
|
|
try {
|
|
ServerSocket serverSocket = new ServerSocket(0);
|
|
|
|
int var1;
|
|
try {
|
|
var1 = serverSocket.getLocalPort();
|
|
} catch (Throwable var4) {
|
|
try {
|
|
serverSocket.close();
|
|
} catch (Throwable var3) {
|
|
var4.addSuppressed(var3);
|
|
}
|
|
|
|
throw var4;
|
|
}
|
|
|
|
serverSocket.close();
|
|
return var1;
|
|
} catch (IOException var5) {
|
|
return 25564;
|
|
}
|
|
}
|
|
|
|
public static boolean isPortAvailable(int port) {
|
|
if (port >= 0 && port <= 65535) {
|
|
try {
|
|
ServerSocket serverSocket = new ServerSocket(port);
|
|
|
|
boolean var2;
|
|
try {
|
|
var2 = serverSocket.getLocalPort() == port;
|
|
} catch (Throwable var5) {
|
|
try {
|
|
serverSocket.close();
|
|
} catch (Throwable var4) {
|
|
var5.addSuppressed(var4);
|
|
}
|
|
|
|
throw var5;
|
|
}
|
|
|
|
serverSocket.close();
|
|
return var2;
|
|
} catch (IOException var6) {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public interface DownloadProgressListener {
|
|
void requestStart();
|
|
|
|
void downloadStart(OptionalLong totalSize);
|
|
|
|
void downloadedBytes(long progress);
|
|
|
|
void requestFinished(boolean success);
|
|
}
|
|
}
|