diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index a74a14a3fa..3808d68ce2 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -14,6 +14,7 @@ use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::UpdateGuard; use deno_ast::MediaType; +use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::custom_error; use deno_core::error::generic_error; @@ -372,6 +373,7 @@ impl FileFetcher { } async move { + let mut maybe_etag = maybe_etag; let mut retried = false; let result = loop { let result = match fetch_once( @@ -388,9 +390,23 @@ impl FileFetcher { { FetchOnceResult::NotModified => { let file = file_fetcher - .fetch_cached(&specifier, maybe_checksum, 10)? - .unwrap(); - Ok(file) + .fetch_cached(&specifier, maybe_checksum.clone(), 10)?; + match file { + Some(file) => Ok(file), + None => { + // Someone may have deleted the body from the cache since + // it's currently stored in a separate file from the headers, + // so delete the etag and try again + if maybe_etag.is_some() { + debug!("Cache body not found. Trying again without etag."); + maybe_etag = None; + continue; + } else { + // should never happen + bail!("Your deno cache directory is in an unrecoverable state. Please delete it and try again.") + } + } + } } FetchOnceResult::Redirect(redirect_url, headers) => { file_fetcher.http_cache.set(&specifier, headers, &[])?; diff --git a/tests/integration/run_tests.rs b/tests/integration/run_tests.rs index 1d46cd249d..59163bfe81 100644 --- a/tests/integration/run_tests.rs +++ b/tests/integration/run_tests.rs @@ -5137,3 +5137,36 @@ console.log(add(3, 4)); let output = test_context.new_command().args("run main.ts").run(); output.assert_matches_text("[WILDCARD]5\n7\n"); } + +#[test] +fn run_etag_delete_source_cache() { + let test_context = TestContextBuilder::new() + .use_temp_cwd() + .use_http_server() + .build(); + test_context + .temp_dir() + .write("main.ts", "import 'http://localhost:4545/etag_script.ts'"); + test_context + .new_command() + .args("cache main.ts") + .run() + .skip_output_check(); + + // The cache is currently stored unideally in two files where one file has the headers + // and the other contains the body. An issue can happen with the etag header where the + // headers file exists, but the body was deleted. We need to get the cache to gracefully + // handle this scenario. + let deno_dir = test_context.deno_dir().path(); + let etag_script_path = deno_dir.join("deps/http/localhost_PORT4545/26110db7d42c9bad32386735cbc05c301f83e4393963deb8da14fec3b4202a13"); + assert!(etag_script_path.exists()); + etag_script_path.remove_file(); + + test_context + .new_command() + .args("cache --reload --log-level=debug main.ts") + .run() + .assert_matches_text( + "[WILDCARD]Cache body not found. Trying again without etag.[WILDCARD]", + ); +}