minecraft-src/net/minecraft/util/HttpUtil.java
2025-07-04 01:41:11 +03:00

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