From 5005343d00114edd3178afa6ea0e6bd4c79c029f Mon Sep 17 00:00:00 2001 From: Glavo Date: Tue, 16 Sep 2025 15:15:30 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=96=87=E4=BB=B6=E6=97=B6?= =?UTF-8?q?=E9=81=B5=E5=BE=AA=20Cache-Control=20=E8=AE=BE=E7=BD=AE=20(#446?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jackhuang/hmcl/task/CacheFileTask.java | 6 +- .../org/jackhuang/hmcl/task/FetchTask.java | 18 +++- .../jackhuang/hmcl/util/CacheRepository.java | 88 ++++++++++++++++--- 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java index a0658eea1..38a99629b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/CacheFileTask.java @@ -17,6 +17,7 @@ */ package org.jackhuang.hmcl.task; +import org.jackhuang.hmcl.util.CacheRepository; import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jetbrains.annotations.NotNull; @@ -51,8 +52,11 @@ public final class CacheFileTask extends FetchTask { // Check cache for (URI uri : uris) { try { - setResult(repository.getCachedRemoteFile(uri)); + setResult(repository.getCachedRemoteFile(uri, true)); + LOG.info("Using cached file for " + NetworkUtils.dropQuery(uri)); return EnumCheckETag.CACHED; + } catch (CacheRepository.CacheExpiredException e) { + LOG.info("Cache expired for " + NetworkUtils.dropQuery(uri)); } catch (IOException ignored) { } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java index 61e9806a9..dcc198275 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FetchTask.java @@ -100,6 +100,17 @@ public abstract class FetchTask extends Task { int repeat = 0; download: for (URI uri : uris) { + if (checkETag) { + // Handle cache + try { + Path cache = repository.getCachedRemoteFile(uri, true); + useCachedResult(cache); + LOG.info("Using cached file for " + NetworkUtils.dropQuery(uri)); + return; + } catch (IOException ignored) { + } + } + for (int retryTime = 0; retryTime < retry; retryTime++) { if (isCancelled()) { break download; @@ -113,8 +124,7 @@ public abstract class FetchTask extends Task { URLConnection conn = NetworkUtils.createConnection(uri); - if (conn instanceof HttpURLConnection) { - var httpConnection = (HttpURLConnection) conn; + if (conn instanceof HttpURLConnection httpConnection) { httpConnection.setRequestProperty("Accept-Encoding", "gzip"); if (checkETag) repository.injectConnection(httpConnection); @@ -172,10 +182,12 @@ public abstract class FetchTask extends Task { if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) { // Handle cache try { - Path cache = repository.getCachedRemoteFile(NetworkUtils.toURI(conn.getURL())); + Path cache = repository.getCachedRemoteFile(NetworkUtils.toURI(conn.getURL()), false); useCachedResult(cache); LOG.info("Using cached file for " + NetworkUtils.dropQuery(uri)); return; + } catch (CacheRepository.CacheExpiredException e) { + LOG.info("Cache expired for " + NetworkUtils.dropQuery(uri)); } catch (IOException e) { LOG.warning("Unable to use cached file, redownload " + NetworkUtils.dropQuery(uri), e); repository.removeRemoteEntry(conn.getURL().toURI()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java index 8ceeccb70..8e410470b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/CacheRepository.java @@ -36,12 +36,15 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.nio.file.attribute.FileTime; +import java.time.Instant; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.BiFunction; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static java.nio.charset.StandardCharsets.UTF_8; import static org.jackhuang.hmcl.util.gson.JsonUtils.*; @@ -150,7 +153,7 @@ public class CacheRepository { return cache; } - public Path getCachedRemoteFile(URI uri) throws IOException { + public Path getCachedRemoteFile(URI uri, boolean checkExpires) throws IOException { lock.readLock().lock(); ETagItem eTagItem; try { @@ -160,6 +163,9 @@ public class CacheRepository { } if (eTagItem == null) throw new IOException("Cannot find the URL"); if (StringUtils.isBlank(eTagItem.hash) || !fileExists(SHA1, eTagItem.hash)) throw new FileNotFoundException(); + if (checkExpires && System.currentTimeMillis() > eTagItem.expires) + throw new CacheExpiredException(eTagItem.expires); + Path file = getFile(SHA1, eTagItem.hash); if (Files.getLastModifiedTime(file).toMillis() != eTagItem.localLastModified) { String hash = DigestUtils.digestToString(SHA1, file); @@ -222,6 +228,8 @@ public class CacheRepository { }); } + private static final Pattern MAX_AGE = Pattern.compile("(s-maxage|max-age)=(?