summaryrefslogtreecommitdiff
path: root/cli/file_fetcher.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/file_fetcher.rs')
-rw-r--r--cli/file_fetcher.rs100
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(),