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