Skip to content

Commit 876a960

Browse files
authored
fix(cache): skip checksums for cached 404 entries (#35526)
Closes #35525. ## What changed Cached 404 entries are now recognized before applying a content checksum in the file fetcher cache path. The checksum is still applied to real cached file bodies, but synthetic not-found metadata entries report `NotFound` instead of trying to validate an empty body against a JSR package manifest checksum. ## Why JSR packages can contain dependency probes for extensionless Node built-ins such as `crypto`, `fs`, or `path`. Those URLs are cached as 404 metadata entries. On a later run, the module graph can provide a package-manifest checksum for the missing JSR subpath, which previously caused the cached 404's empty body to fail integrity validation with `Expected: package-manifest-missing-checksum`. ## Validation - `cargo fmt -p deno_cache_dir` - `cargo test -p deno_cache_dir test_fetch_not_found_is_negatively_cached` - `cargo test -p deno_cache_dir` - `./tools/lint.js` - End-to-end: first and second `cargo run --quiet --bin deno -- cache jsr:@ts-morph/ts-morph@28.0.0` with the same temporary `DENO_DIR` both exited 0
1 parent 8025886 commit 876a960

2 files changed

Lines changed: 37 additions & 12 deletions

File tree

libs/cache_dir/file_fetcher/mod.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -719,20 +719,33 @@ impl<TBlobStore: BlobStore, TSys: FileFetcherSys, THttpClient: HttpClient>
719719
url: url.clone(),
720720
source,
721721
})?;
722+
let maybe_headers =
723+
self.http_cache.read_headers(&cache_key).map_err(|source| {
724+
CacheReadError {
725+
url: url.clone(),
726+
source,
727+
}
728+
})?;
729+
if let Some(headers) = &maybe_headers
730+
&& headers.contains_key(NOT_FOUND_CACHE_HEADER)
731+
{
732+
let download_time = self
733+
.http_cache
734+
.read_download_time(&cache_key)
735+
.map_err(|source| CacheReadError {
736+
url: url.clone(),
737+
source,
738+
})?;
739+
return if self.is_not_found_entry_fresh(download_time) {
740+
Err(FetchCachedNoFollowErrorKind::NotFound(url.clone()).into_box())
741+
} else {
742+
// expired, treat as a cache miss so the URL is re-requested
743+
Ok(None)
744+
};
745+
}
746+
722747
match self.http_cache.get(&cache_key, maybe_checksum) {
723748
Ok(Some(entry)) => {
724-
if entry.metadata.headers.contains_key(NOT_FOUND_CACHE_HEADER) {
725-
let download_time = entry
726-
.metadata
727-
.time
728-
.map(|secs| SystemTime::UNIX_EPOCH + Duration::from_secs(secs));
729-
return if self.is_not_found_entry_fresh(download_time) {
730-
Err(FetchCachedNoFollowErrorKind::NotFound(url.clone()).into_box())
731-
} else {
732-
// expired, treat as a cache miss so the URL is re-requested
733-
Ok(None)
734-
};
735-
}
736749
Ok(Some(FileOrRedirect::from_deno_cache_entry(url, entry)?))
737750
}
738751
Ok(None) => Ok(None),

libs/cache_dir/tests/file_fetcher_test.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use std::time::SystemTime;
66

7+
use deno_cache_dir::Checksum;
78
use deno_cache_dir::file_fetcher::AuthTokens;
89
use deno_cache_dir::file_fetcher::CacheSetting;
910
use deno_cache_dir::file_fetcher::CachedOrRedirect;
@@ -399,6 +400,17 @@ async fn test_fetch_not_found_is_negatively_cached() {
399400
.fetch_no_follow(&url, FetchNoFollowOptions::default())
400401
.await,
401402
);
403+
assert_not_found(
404+
file_fetcher
405+
.fetch_no_follow(
406+
&url,
407+
FetchNoFollowOptions {
408+
maybe_checksum: Some(Checksum::new("not matching")),
409+
..Default::default()
410+
},
411+
)
412+
.await,
413+
);
402414
match file_fetcher
403415
.ensure_cached_no_follow(&url, FetchNoFollowOptions::default())
404416
.await

0 commit comments

Comments
 (0)