diff --git a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java index accbccdf..e19cce09 100644 --- a/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java +++ b/src/main/java/net/fabricmc/loom/task/DownloadAssetsTask.java @@ -102,7 +102,6 @@ public abstract class DownloadAssetsTask extends AbstractLoomTask { .download(url) .sha1(sha1) .progress(new GradleDownloadProgressListener(object.name(), progressGroup::createProgressLogger)) - .maxRetries(3) .downloadPathAsync(getAssetsPath(object, assetIndex), executor); } } diff --git a/src/main/java/net/fabricmc/loom/util/download/Download.java b/src/main/java/net/fabricmc/loom/util/download/Download.java index 12742ce6..b0a2dd6e 100644 --- a/src/main/java/net/fabricmc/loom/util/download/Download.java +++ b/src/main/java/net/fabricmc/loom/util/download/Download.java @@ -25,6 +25,7 @@ package net.fabricmc.loom.util.download; import java.io.IOException; +import java.io.InputStream; import java.io.UncheckedIOException; import java.net.HttpURLConnection; import java.net.ProxySelector; @@ -45,8 +46,8 @@ import java.time.Instant; import java.util.Locale; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.GZIPInputStream; -import com.github.mizosoft.methanol.Methanol; import com.github.mizosoft.methanol.ProgressTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,10 +86,9 @@ public class Download { throw error("Unable to download %s in offline mode", this.url); } - return Methanol.newBuilder() + return HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.ALWAYS) .proxy(ProxySelector.getDefault()) - .autoAcceptEncoding(true) .build(); } @@ -127,7 +127,7 @@ public class Download { } String downloadString() throws DownloadException { - final HttpResponse response = send(getRequest(), HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + final HttpResponse response = send(getRequest(), HttpResponse.BodyHandlers.ofInputStream()); final int statusCode = response.statusCode(); final boolean successful = statusCode >= 200 && statusCode < 300; @@ -135,7 +135,11 @@ public class Download { throw error("HTTP request to (%s) returned unsuccessful status (%d)", url, statusCode); } - return response.body(); + try (InputStream inputStream = decodeOutput(response)) { + return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); + } catch (IOException e) { + throw error(e, "Failed to decode download output"); + } } void downloadPath(Path output) throws DownloadException { @@ -174,7 +178,7 @@ public class Download { // Create a .lock file, this allows us to re-download if the download was forcefully aborted part way through. createLock(output); - HttpResponse response = send(httpRequest, HttpResponse.BodyHandlers.ofFile(output)); + HttpResponse response = send(httpRequest, HttpResponse.BodyHandlers.ofInputStream()); getAndResetLock(output); final int statusCode = response.statusCode(); @@ -185,13 +189,15 @@ public class Download { return; } - if (!success) { - try { - Files.deleteIfExists(output); - } catch (IOException ignored) { - // We tried. + if (success) { + try (InputStream inputStream = decodeOutput(response)) { + Files.write(output, inputStream.readAllBytes()); + } catch (IOException e) { + tryCleanup(output); + throw error(e, "Failed to decode and write download output"); } - + } else { + tryCleanup(output); throw error("HTTP request to (%s) returned unsuccessful status (%d)", url, statusCode); } @@ -224,6 +230,16 @@ public class Download { } } + private InputStream decodeOutput(HttpResponse response) throws IOException { + final String encoding = response.headers().firstValue("Content-Encoding").orElse(""); + + return switch (encoding) { + case "gzip" -> new GZIPInputStream(response.body()); + case "" -> response.body(); + default -> throw error("Unsupported encoding: %s", encoding); + }; + } + private boolean requiresDownload(Path output) throws DownloadException { if (getAndResetLock(output)) { LOGGER.warn("Forcing downloading {} as existing lock file was found. This may happen if the gradle build was forcefully canceled.", output); diff --git a/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java b/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java index 5a10f413..95d46623 100644 --- a/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java +++ b/src/main/java/net/fabricmc/loom/util/download/DownloadBuilder.java @@ -44,7 +44,7 @@ public class DownloadBuilder { private boolean offline = false; private Duration maxAge = Duration.ZERO; private DownloadProgressListener progressListener = DownloadProgressListener.NONE; - private int maxRetries = 1; + private int maxRetries = 3; private DownloadBuilder(URI url) { this.url = url; diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy index 451c3edc..8815a146 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/download/DownloadFileTest.groovy @@ -25,6 +25,7 @@ package net.fabricmc.loom.test.unit.download import io.javalin.http.HttpCode +import net.fabricmc.loom.util.Checksum import net.fabricmc.loom.util.download.Download import net.fabricmc.loom.util.download.DownloadException import net.fabricmc.loom.util.download.DownloadExecutor @@ -302,4 +303,35 @@ class DownloadFileTest extends DownloadTest { then: thrown DownloadException } + + def "File: Large"() { + setup: + byte[] data = new byte[1024 * 1024 * 10] // 10MB + new Random().nextBytes(data) + + server.get("/largeFile") { + it.result(data) + } + + def output = new File(File.createTempDir(), "file").toPath() + + when: + def result = Download.create("$PATH/largeFile").downloadPath(output) + + then: + Files.readAllBytes(output) == data + } + + // Known + def "Download Mojang Mappings"() { + setup: + def file = File.createTempDir().toPath().resolve("client.txt") + when: + Download.create("https://piston-data.mojang.com/v1/objects/8e8c9be5dc27802caba47053d4fdea328f7f89bd/client.txt") + .sha1("8e8c9be5dc27802caba47053d4fdea328f7f89bd") + .downloadPath(file) + + then: + Checksum.sha1Hex(file) == "8e8c9be5dc27802caba47053d4fdea328f7f89bd" + } }