diff options
author | Kitson Kelly <me@kitsonkelly.com> | 2021-12-09 22:16:17 +1100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-09 22:16:17 +1100 |
commit | 2347e60934a8701bb5f0ecba6a460447eab868c6 (patch) | |
tree | 9f857060a2d1c4a0536557ac3f0fad89b3d07ece /cli/file_fetcher.rs | |
parent | 69ad5f0e7879e9555f949c9b5eb48440cd9e9fdc (diff) |
feat(lsp): registry suggestion cache respects cache headers (#13010)
Fixes #9931
Diffstat (limited to 'cli/file_fetcher.rs')
-rw-r--r-- | cli/file_fetcher.rs | 100 |
1 files changed, 87 insertions, 13 deletions
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 924076872..e8ad2ccb2 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -4,10 +4,12 @@ use crate::auth_tokens::AuthTokens; use crate::colors; use crate::http_cache::HttpCache; use crate::http_util::fetch_once; +use crate::http_util::CacheSemantics; use crate::http_util::FetchOnceArgs; use crate::http_util::FetchOnceResult; use crate::text_encoding; use crate::version::get_user_agent; + use data_url::DataUrl; use deno_ast::MediaType; use deno_core::error::custom_error; @@ -34,6 +36,7 @@ use std::io::Read; use std::path::PathBuf; use std::pin::Pin; use std::sync::Arc; +use std::time::SystemTime; pub const SUPPORTED_SCHEMES: [&str; 5] = ["data", "blob", "file", "http", "https"]; @@ -89,6 +92,10 @@ pub enum CacheSetting { /// `--reload=https://deno.land/std` or /// `--reload=https://deno.land/std,https://deno.land/x/example`. ReloadSome(Vec<String>), + /// The usability of a cached value is determined by analyzing the cached + /// headers and other metadata associated with a cached response, reloading + /// any cached "non-fresh" cached responses. + RespectHeaders, /// The cached source files should be used for local modules. This is the /// default behavior of the CLI. Use, @@ -96,10 +103,23 @@ pub enum CacheSetting { impl CacheSetting { /// Returns if the cache should be used for a given specifier. - pub fn should_use(&self, specifier: &ModuleSpecifier) -> bool { + pub fn should_use( + &self, + specifier: &ModuleSpecifier, + http_cache: &HttpCache, + ) -> bool { match self { CacheSetting::ReloadAll => false, CacheSetting::Use | CacheSetting::Only => true, + CacheSetting::RespectHeaders => { + if let Ok((_, headers, cache_time)) = http_cache.get(specifier) { + let cache_semantics = + CacheSemantics::new(headers, cache_time, SystemTime::now()); + cache_semantics.should_use() + } else { + false + } + } CacheSetting::ReloadSome(list) => { let mut url = specifier.clone(); url.set_fragment(None); @@ -312,7 +332,7 @@ impl FileFetcher { return Err(custom_error("Http", "Too many redirects.")); } - let (mut source_file, headers) = match self.http_cache.get(specifier) { + let (mut source_file, headers, _) = match self.http_cache.get(specifier) { Err(err) => { if let Some(err) = err.downcast_ref::<std::io::Error>() { if err.kind() == std::io::ErrorKind::NotFound { @@ -469,7 +489,7 @@ impl FileFetcher { return futures::future::err(err).boxed(); } - if self.cache_setting.should_use(specifier) { + if self.cache_setting.should_use(specifier, &self.http_cache) { match self.fetch_cached(specifier, redirect_limit) { Ok(Some(file)) => { return futures::future::ok(file).boxed(); @@ -495,7 +515,7 @@ impl FileFetcher { info!("{} {}", colors::green("Download"), specifier); let maybe_etag = match self.http_cache.get(specifier) { - Ok((_, headers)) => headers.get("etag").cloned(), + Ok((_, headers, _)) => headers.get("etag").cloned(), _ => None, }; let maybe_auth_token = self.auth_tokens.get(specifier); @@ -682,7 +702,7 @@ mod tests { .fetch_remote(specifier, &mut Permissions::allow_all(), 1) .await; assert!(result.is_ok()); - let (_, headers) = file_fetcher.http_cache.get(specifier).unwrap(); + let (_, headers, _) = file_fetcher.http_cache.get(specifier).unwrap(); (result.unwrap(), headers) } @@ -1065,7 +1085,7 @@ mod tests { // the value above. assert_eq!(file.media_type, MediaType::JavaScript); - let (_, headers) = file_fetcher_02.http_cache.get(&specifier).unwrap(); + let (_, headers, _) = file_fetcher_02.http_cache.get(&specifier).unwrap(); assert_eq!(headers.get("content-type").unwrap(), "text/javascript"); metadata.headers = HashMap::new(); metadata @@ -1194,7 +1214,7 @@ mod tests { "", "redirected files should have empty cached contents" ); - let (_, headers) = file_fetcher.http_cache.get(&specifier).unwrap(); + let (_, headers, _) = file_fetcher.http_cache.get(&specifier).unwrap(); assert_eq!( headers.get("location").unwrap(), "http://localhost:4545/subdir/redirects/redirect1.js" @@ -1204,7 +1224,7 @@ mod tests { fs::read_to_string(redirected_cached_filename).unwrap(), "export const redirect = 1;\n" ); - let (_, headers) = + let (_, headers, _) = file_fetcher.http_cache.get(&redirected_specifier).unwrap(); assert!(headers.get("location").is_none()); } @@ -1247,7 +1267,7 @@ mod tests { "", "redirected files should have empty cached contents" ); - let (_, headers) = file_fetcher.http_cache.get(&specifier).unwrap(); + let (_, headers, _) = file_fetcher.http_cache.get(&specifier).unwrap(); assert_eq!( headers.get("location").unwrap(), "http://localhost:4546/subdir/redirects/redirect1.js" @@ -1258,7 +1278,7 @@ mod tests { "", "redirected files should have empty cached contents" ); - let (_, headers) = file_fetcher + let (_, headers, _) = file_fetcher .http_cache .get(&redirected_01_specifier) .unwrap(); @@ -1271,7 +1291,7 @@ mod tests { fs::read_to_string(redirected_02_cached_filename).unwrap(), "export const redirect = 1;\n" ); - let (_, headers) = file_fetcher + let (_, headers, _) = file_fetcher .http_cache .get(&redirected_02_specifier) .unwrap(); @@ -1392,7 +1412,7 @@ mod tests { "", "redirected files should have empty cached contents" ); - let (_, headers) = file_fetcher.http_cache.get(&specifier).unwrap(); + let (_, headers, _) = file_fetcher.http_cache.get(&specifier).unwrap(); assert_eq!( headers.get("location").unwrap(), "/subdir/redirects/redirect1.js" @@ -1402,7 +1422,7 @@ mod tests { fs::read_to_string(redirected_cached_filename).unwrap(), "export const redirect = 1;\n" ); - let (_, headers) = + let (_, headers, _) = file_fetcher.http_cache.get(&redirected_specifier).unwrap(); assert!(headers.get("location").is_none()); } @@ -1500,6 +1520,60 @@ mod tests { } #[tokio::test] + async fn test_respect_cache_revalidates() { + let _g = test_util::http_server(); + let temp_dir = Rc::new(TempDir::new().unwrap()); + let (file_fetcher, _) = + setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); + let specifier = + ModuleSpecifier::parse("http://localhost:4545/dynamic").unwrap(); + let result = file_fetcher + .fetch(&specifier, &mut Permissions::allow_all()) + .await; + assert!(result.is_ok()); + let file = result.unwrap(); + let first = file.source.as_str(); + + let (file_fetcher, _) = + setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); + let result = file_fetcher + .fetch(&specifier, &mut Permissions::allow_all()) + .await; + assert!(result.is_ok()); + let file = result.unwrap(); + let second = file.source.as_str(); + + assert_ne!(first, second); + } + + #[tokio::test] + async fn test_respect_cache_still_fresh() { + let _g = test_util::http_server(); + let temp_dir = Rc::new(TempDir::new().unwrap()); + let (file_fetcher, _) = + setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); + let specifier = + ModuleSpecifier::parse("http://localhost:4545/dynamic_cache").unwrap(); + let result = file_fetcher + .fetch(&specifier, &mut Permissions::allow_all()) + .await; + assert!(result.is_ok()); + let file = result.unwrap(); + let first = file.source.as_str(); + + let (file_fetcher, _) = + setup(CacheSetting::RespectHeaders, Some(temp_dir.clone())); + let result = file_fetcher + .fetch(&specifier, &mut Permissions::allow_all()) + .await; + assert!(result.is_ok()); + let file = result.unwrap(); + let second = file.source.as_str(); + + assert_eq!(first, second); + } + + #[tokio::test] async fn test_fetch_local_utf_16be() { let expected = String::from_utf8( b"\xEF\xBB\xBFconsole.log(\"Hello World\");\x0A".to_vec(), |