1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

fix(npm): use an http client with connection pool (#16705)

Should make downloading npm packages faster and more reliable.
This commit is contained in:
David Sherret 2022-11-18 17:28:14 -05:00 committed by GitHub
parent 6962808f4b
commit 763d492ed6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 454 additions and 422 deletions

View file

@ -3,13 +3,12 @@
use crate::auth_tokens::AuthTokens; use crate::auth_tokens::AuthTokens;
use crate::colors; use crate::colors;
use crate::http_cache::HttpCache; use crate::http_cache::HttpCache;
use crate::http_util::fetch_once;
use crate::http_util::CacheSemantics; use crate::http_util::CacheSemantics;
use crate::http_util::FetchOnceArgs; use crate::http_util::FetchOnceArgs;
use crate::http_util::FetchOnceResult; use crate::http_util::FetchOnceResult;
use crate::http_util::HttpClient;
use crate::progress_bar::ProgressBar; use crate::progress_bar::ProgressBar;
use crate::text_encoding; use crate::text_encoding;
use crate::version::get_user_agent;
use data_url::DataUrl; use data_url::DataUrl;
use deno_ast::MediaType; use deno_ast::MediaType;
@ -22,8 +21,6 @@ use deno_core::futures;
use deno_core::futures::future::FutureExt; use deno_core::futures::future::FutureExt;
use deno_core::parking_lot::Mutex; use deno_core::parking_lot::Mutex;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_runtime::deno_fetch::create_http_client;
use deno_runtime::deno_fetch::reqwest;
use deno_runtime::deno_tls::rustls; use deno_runtime::deno_tls::rustls;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::rustls_native_certs::load_native_certs; use deno_runtime::deno_tls::rustls_native_certs::load_native_certs;
@ -333,7 +330,7 @@ pub struct FileFetcher {
cache: FileCache, cache: FileCache,
cache_setting: CacheSetting, cache_setting: CacheSetting,
pub http_cache: HttpCache, pub http_cache: HttpCache,
http_client: reqwest::Client, http_client: HttpClient,
blob_store: BlobStore, blob_store: BlobStore,
download_log_level: log::Level, download_log_level: log::Level,
progress_bar: Option<ProgressBar>, progress_bar: Option<ProgressBar>,
@ -344,9 +341,8 @@ impl FileFetcher {
http_cache: HttpCache, http_cache: HttpCache,
cache_setting: CacheSetting, cache_setting: CacheSetting,
allow_remote: bool, allow_remote: bool,
root_cert_store: Option<RootCertStore>, http_client: HttpClient,
blob_store: BlobStore, blob_store: BlobStore,
unsafely_ignore_certificate_errors: Option<Vec<String>>,
progress_bar: Option<ProgressBar>, progress_bar: Option<ProgressBar>,
) -> Result<Self, AnyError> { ) -> Result<Self, AnyError> {
Ok(Self { Ok(Self {
@ -355,14 +351,7 @@ impl FileFetcher {
cache: Default::default(), cache: Default::default(),
cache_setting, cache_setting,
http_cache, http_cache,
http_client: create_http_client( http_client,
get_user_agent(),
root_cert_store,
vec![],
None,
unsafely_ignore_certificate_errors,
None,
)?,
blob_store, blob_store,
download_log_level: log::Level::Info, download_log_level: log::Level::Info,
progress_bar, progress_bar,
@ -628,8 +617,8 @@ impl FileFetcher {
let file_fetcher = self.clone(); let file_fetcher = self.clone();
// A single pass of fetch either yields code or yields a redirect. // A single pass of fetch either yields code or yields a redirect.
async move { async move {
match fetch_once(FetchOnceArgs { match client
client, .fetch_once(FetchOnceArgs {
url: specifier.clone(), url: specifier.clone(),
maybe_accept: maybe_accept.clone(), maybe_accept: maybe_accept.clone(),
maybe_etag, maybe_etag,
@ -765,6 +754,8 @@ impl FileFetcher {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::http_util::HttpClient;
use super::*; use super::*;
use deno_core::error::get_custom_error_class; use deno_core::error::get_custom_error_class;
use deno_core::resolve_url; use deno_core::resolve_url;
@ -793,10 +784,9 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
cache_setting, cache_setting,
true, true,
None, HttpClient::new(None, None).unwrap(),
blob_store.clone(), blob_store.clone(),
None, None,
None,
) )
.unwrap(); .unwrap();
(file_fetcher, temp_dir, blob_store) (file_fetcher, temp_dir, blob_store)
@ -1232,10 +1222,9 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::ReloadAll, CacheSetting::ReloadAll,
true, true,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let result = file_fetcher let result = file_fetcher
@ -1259,10 +1248,9 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let specifier = let specifier =
@ -1287,10 +1275,9 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let result = file_fetcher_02 let result = file_fetcher_02
@ -1431,10 +1418,9 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let specifier = let specifier =
@ -1461,10 +1447,9 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let result = file_fetcher_02 let result = file_fetcher_02
@ -1562,10 +1547,9 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
false, false,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let specifier = let specifier =
@ -1589,20 +1573,18 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Only, CacheSetting::Only,
true, true,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let file_fetcher_02 = FileFetcher::new( let file_fetcher_02 = FileFetcher::new(
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
None, HttpClient::new(None, None).unwrap(),
BlobStore::default(), BlobStore::default(),
None, None,
None,
) )
.unwrap(); .unwrap();
let specifier = let specifier =

View file

@ -73,11 +73,6 @@ pub fn url_to_filename(url: &Url) -> Option<PathBuf> {
Some(cache_filename) Some(cache_filename)
} }
#[derive(Debug, Clone, Default)]
pub struct HttpCache {
pub location: PathBuf,
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Metadata { pub struct Metadata {
pub headers: HeadersMap, pub headers: HeadersMap,
@ -107,6 +102,11 @@ impl Metadata {
} }
} }
#[derive(Debug, Clone, Default)]
pub struct HttpCache {
pub location: PathBuf,
}
impl HttpCache { impl HttpCache {
/// Returns a new instance. /// Returns a new instance.
/// ///

View file

@ -1,5 +1,6 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use crate::auth_tokens::AuthToken; use crate::auth_tokens::AuthToken;
use crate::version::get_user_agent;
use cache_control::Cachability; use cache_control::Cachability;
use cache_control::CacheControl; use cache_control::CacheControl;
@ -8,13 +9,15 @@ use deno_core::error::custom_error;
use deno_core::error::generic_error; use deno_core::error::generic_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::deno_fetch::create_http_client;
use deno_runtime::deno_fetch::reqwest;
use deno_runtime::deno_fetch::reqwest::header::HeaderValue; use deno_runtime::deno_fetch::reqwest::header::HeaderValue;
use deno_runtime::deno_fetch::reqwest::header::ACCEPT; use deno_runtime::deno_fetch::reqwest::header::ACCEPT;
use deno_runtime::deno_fetch::reqwest::header::AUTHORIZATION; use deno_runtime::deno_fetch::reqwest::header::AUTHORIZATION;
use deno_runtime::deno_fetch::reqwest::header::IF_NONE_MATCH; use deno_runtime::deno_fetch::reqwest::header::IF_NONE_MATCH;
use deno_runtime::deno_fetch::reqwest::header::LOCATION; use deno_runtime::deno_fetch::reqwest::header::LOCATION;
use deno_runtime::deno_fetch::reqwest::Client;
use deno_runtime::deno_fetch::reqwest::StatusCode; use deno_runtime::deno_fetch::reqwest::StatusCode;
use deno_runtime::deno_tls::rustls::RootCertStore;
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration; use std::time::Duration;
@ -208,22 +211,48 @@ pub enum FetchOnceResult {
#[derive(Debug)] #[derive(Debug)]
pub struct FetchOnceArgs { pub struct FetchOnceArgs {
pub client: Client,
pub url: Url, pub url: Url,
pub maybe_accept: Option<String>, pub maybe_accept: Option<String>,
pub maybe_etag: Option<String>, pub maybe_etag: Option<String>,
pub maybe_auth_token: Option<AuthToken>, pub maybe_auth_token: Option<AuthToken>,
} }
#[derive(Debug, Clone)]
pub struct HttpClient(reqwest::Client);
impl HttpClient {
pub fn new(
root_cert_store: Option<RootCertStore>,
unsafely_ignore_certificate_errors: Option<Vec<String>>,
) -> Result<Self, AnyError> {
Ok(HttpClient::from_client(create_http_client(
get_user_agent(),
root_cert_store,
vec![],
None,
unsafely_ignore_certificate_errors,
None,
)?))
}
pub fn from_client(client: reqwest::Client) -> Self {
Self(client)
}
pub fn get<U: reqwest::IntoUrl>(&self, url: U) -> reqwest::RequestBuilder {
self.0.get(url)
}
/// Asynchronously fetches the given HTTP URL one pass only. /// Asynchronously fetches the given HTTP URL one pass only.
/// If no redirect is present and no error occurs, /// If no redirect is present and no error occurs,
/// yields Code(ResultPayload). /// yields Code(ResultPayload).
/// If redirect occurs, does not follow and /// If redirect occurs, does not follow and
/// yields Redirect(url). /// yields Redirect(url).
pub async fn fetch_once( pub async fn fetch_once(
&self,
args: FetchOnceArgs, args: FetchOnceArgs,
) -> Result<FetchOnceResult, AnyError> { ) -> Result<FetchOnceResult, AnyError> {
let mut request = args.client.get(args.url.clone()); let mut request = self.get(args.url.clone());
if let Some(etag) = args.maybe_etag { if let Some(etag) = args.maybe_etag {
let if_none_match_val = HeaderValue::from_str(&etag)?; let if_none_match_val = HeaderValue::from_str(&etag)?;
@ -243,10 +272,10 @@ pub async fn fetch_once(
return Ok(FetchOnceResult::NotModified); return Ok(FetchOnceResult::NotModified);
} }
let mut headers_: HashMap<String, String> = HashMap::new(); let mut result_headers = HashMap::new();
let headers = response.headers(); let response_headers = response.headers();
if let Some(warning) = headers.get("X-Deno-Warning") { if let Some(warning) = response_headers.get("X-Deno-Warning") {
eprintln!( eprintln!(
"{} {}", "{} {}",
crate::colors::yellow("Warning"), crate::colors::yellow("Warning"),
@ -254,15 +283,15 @@ pub async fn fetch_once(
); );
} }
for key in headers.keys() { for key in response_headers.keys() {
let key_str = key.to_string(); let key_str = key.to_string();
let values = headers.get_all(key); let values = response_headers.get_all(key);
let values_str = values let values_str = values
.iter() .iter()
.map(|e| e.to_str().unwrap().to_string()) .map(|e| e.to_str().unwrap().to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join(","); .join(",");
headers_.insert(key_str, values_str); result_headers.insert(key_str, values_str);
} }
if response.status().is_redirection() { if response.status().is_redirection() {
@ -270,7 +299,7 @@ pub async fn fetch_once(
let location_string = location.to_str().unwrap(); let location_string = location.to_str().unwrap();
debug!("Redirecting to {:?}...", &location_string); debug!("Redirecting to {:?}...", &location_string);
let new_url = resolve_url_from_location(&args.url, location_string); let new_url = resolve_url_from_location(&args.url, location_string);
return Ok(FetchOnceResult::Redirect(new_url, headers_)); return Ok(FetchOnceResult::Redirect(new_url, result_headers));
} else { } else {
return Err(generic_error(format!( return Err(generic_error(format!(
"Redirection from '{}' did not provide location header", "Redirection from '{}' did not provide location header",
@ -279,7 +308,8 @@ pub async fn fetch_once(
} }
} }
if response.status().is_client_error() || response.status().is_server_error() if response.status().is_client_error()
|| response.status().is_server_error()
{ {
let err = if response.status() == StatusCode::NOT_FOUND { let err = if response.status() == StatusCode::NOT_FOUND {
custom_error( custom_error(
@ -298,7 +328,8 @@ pub async fn fetch_once(
let body = response.bytes().await?.to_vec(); let body = response.bytes().await?.to_vec();
Ok(FetchOnceResult::Code(body, headers_)) Ok(FetchOnceResult::Code(body, result_headers))
}
} }
#[cfg(test)] #[cfg(test)]
@ -308,7 +339,8 @@ mod tests {
use deno_runtime::deno_fetch::create_http_client; use deno_runtime::deno_fetch::create_http_client;
use std::fs::read; use std::fs::read;
fn create_test_client() -> Client { fn create_test_client() -> HttpClient {
HttpClient::from_client(
create_http_client( create_http_client(
"test_client".to_string(), "test_client".to_string(),
None, None,
@ -317,7 +349,8 @@ mod tests {
None, None,
None, None,
) )
.unwrap() .unwrap(),
)
} }
#[tokio::test] #[tokio::test]
@ -326,8 +359,8 @@ mod tests {
// Relies on external http server. See target/debug/test_server // Relies on external http server. See target/debug/test_server
let url = Url::parse("http://127.0.0.1:4545/assets/fixture.json").unwrap(); let url = Url::parse("http://127.0.0.1:4545/assets/fixture.json").unwrap();
let client = create_test_client(); let client = create_test_client();
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -351,8 +384,8 @@ mod tests {
let url = Url::parse("http://127.0.0.1:4545/run/import_compression/gziped") let url = Url::parse("http://127.0.0.1:4545/run/import_compression/gziped")
.unwrap(); .unwrap();
let client = create_test_client(); let client = create_test_client();
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -377,8 +410,8 @@ mod tests {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); let url = Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap();
let client = create_test_client(); let client = create_test_client();
let result = fetch_once(FetchOnceArgs { let result = client
client: client.clone(), .fetch_once(FetchOnceArgs {
url: url.clone(), url: url.clone(),
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -397,8 +430,8 @@ mod tests {
panic!(); panic!();
} }
let res = fetch_once(FetchOnceArgs { let res = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: Some("33a64df551425fcc55e".to_string()), maybe_etag: Some("33a64df551425fcc55e".to_string()),
@ -415,8 +448,8 @@ mod tests {
let url = Url::parse("http://127.0.0.1:4545/run/import_compression/brotli") let url = Url::parse("http://127.0.0.1:4545/run/import_compression/brotli")
.unwrap(); .unwrap();
let client = create_test_client(); let client = create_test_client();
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -443,8 +476,8 @@ mod tests {
// Relies on external http server. See target/debug/test_server // Relies on external http server. See target/debug/test_server
let url = Url::parse("http://127.0.0.1:4545/echo_accept").unwrap(); let url = Url::parse("http://127.0.0.1:4545/echo_accept").unwrap();
let client = create_test_client(); let client = create_test_client();
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: Some("application/json".to_string()), maybe_accept: Some("application/json".to_string()),
maybe_etag: None, maybe_etag: None,
@ -467,8 +500,8 @@ mod tests {
let target_url = let target_url =
Url::parse("http://localhost:4545/assets/fixture.json").unwrap(); Url::parse("http://localhost:4545/assets/fixture.json").unwrap();
let client = create_test_client(); let client = create_test_client();
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -526,7 +559,8 @@ mod tests {
// Relies on external http server. See target/debug/test_server // Relies on external http server. See target/debug/test_server
let url = Url::parse("https://localhost:5545/assets/fixture.json").unwrap(); let url = Url::parse("https://localhost:5545/assets/fixture.json").unwrap();
let client = create_http_client( let client = HttpClient::from_client(
create_http_client(
version::get_user_agent(), version::get_user_agent(),
None, None,
vec![read( vec![read(
@ -540,9 +574,10 @@ mod tests {
None, None,
None, None,
) )
.unwrap(); .unwrap(),
let result = fetch_once(FetchOnceArgs { );
client, let result = client
.fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -564,7 +599,8 @@ mod tests {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
// Relies on external http server with a valid mozilla root CA cert. // Relies on external http server with a valid mozilla root CA cert.
let url = Url::parse("https://deno.land").unwrap(); let url = Url::parse("https://deno.land").unwrap();
let client = create_http_client( let client = HttpClient::from_client(
create_http_client(
version::get_user_agent(), version::get_user_agent(),
None, // This will load mozilla certs by default None, // This will load mozilla certs by default
vec![], vec![],
@ -572,10 +608,11 @@ mod tests {
None, None,
None, None,
) )
.unwrap(); .unwrap(),
);
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -601,18 +638,14 @@ mod tests {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
// Relies on external http server with a valid mozilla root CA cert. // Relies on external http server with a valid mozilla root CA cert.
let url = Url::parse("https://deno.land").unwrap(); let url = Url::parse("https://deno.land").unwrap();
let client = create_http_client( let client = HttpClient::new(
version::get_user_agent(),
Some(RootCertStore::empty()), // no certs loaded at all Some(RootCertStore::empty()), // no certs loaded at all
vec![],
None,
None,
None, None,
) )
.unwrap(); .unwrap();
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -633,7 +666,8 @@ mod tests {
let url = let url =
Url::parse("https://localhost:5545/run/import_compression/gziped") Url::parse("https://localhost:5545/run/import_compression/gziped")
.unwrap(); .unwrap();
let client = create_http_client( let client = HttpClient::from_client(
create_http_client(
version::get_user_agent(), version::get_user_agent(),
None, None,
vec![read( vec![read(
@ -647,9 +681,10 @@ mod tests {
None, None,
None, None,
) )
.unwrap(); .unwrap(),
let result = fetch_once(FetchOnceArgs { );
client, let result = client
.fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -673,7 +708,8 @@ mod tests {
async fn test_fetch_with_cafile_with_etag() { async fn test_fetch_with_cafile_with_etag() {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap(); let url = Url::parse("https://localhost:5545/etag_script.ts").unwrap();
let client = create_http_client( let client = HttpClient::from_client(
create_http_client(
version::get_user_agent(), version::get_user_agent(),
None, None,
vec![read( vec![read(
@ -687,9 +723,10 @@ mod tests {
None, None,
None, None,
) )
.unwrap(); .unwrap(),
let result = fetch_once(FetchOnceArgs { );
client: client.clone(), let result = client
.fetch_once(FetchOnceArgs {
url: url.clone(), url: url.clone(),
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -709,8 +746,8 @@ mod tests {
panic!(); panic!();
} }
let res = fetch_once(FetchOnceArgs { let res = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: Some("33a64df551425fcc55e".to_string()), maybe_etag: Some("33a64df551425fcc55e".to_string()),
@ -727,7 +764,8 @@ mod tests {
let url = let url =
Url::parse("https://localhost:5545/run/import_compression/brotli") Url::parse("https://localhost:5545/run/import_compression/brotli")
.unwrap(); .unwrap();
let client = create_http_client( let client = HttpClient::from_client(
create_http_client(
version::get_user_agent(), version::get_user_agent(),
None, None,
vec![read( vec![read(
@ -741,9 +779,10 @@ mod tests {
None, None,
None, None,
) )
.unwrap(); .unwrap(),
let result = fetch_once(FetchOnceArgs { );
client, let result = client
.fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,
@ -770,8 +809,8 @@ mod tests {
let url_str = "http://127.0.0.1:4545/bad_redirect"; let url_str = "http://127.0.0.1:4545/bad_redirect";
let url = Url::parse(url_str).unwrap(); let url = Url::parse(url_str).unwrap();
let client = create_test_client(); let client = create_test_client();
let result = fetch_once(FetchOnceArgs { let result = client
client, .fetch_once(FetchOnceArgs {
url, url,
maybe_accept: None, maybe_accept: None,
maybe_etag: None, maybe_etag: None,

View file

@ -50,7 +50,6 @@ use super::parent_process_checker;
use super::performance::Performance; use super::performance::Performance;
use super::refactor; use super::refactor;
use super::registries::ModuleRegistry; use super::registries::ModuleRegistry;
use super::registries::ModuleRegistryOptions;
use super::testing; use super::testing;
use super::text; use super::text;
use super::tsc; use super::tsc;
@ -65,10 +64,13 @@ use crate::args::FmtConfig;
use crate::args::LintConfig; use crate::args::LintConfig;
use crate::args::TsConfig; use crate::args::TsConfig;
use crate::deno_dir; use crate::deno_dir;
use crate::deno_dir::DenoDir;
use crate::file_fetcher::get_root_cert_store;
use crate::file_fetcher::get_source_from_data_url; use crate::file_fetcher::get_source_from_data_url;
use crate::file_fetcher::CacheSetting; use crate::file_fetcher::CacheSetting;
use crate::fs_util; use crate::fs_util;
use crate::graph_util::graph_valid; use crate::graph_util::graph_valid;
use crate::http_util::HttpClient;
use crate::npm::NpmCache; use crate::npm::NpmCache;
use crate::npm::NpmPackageResolver; use crate::npm::NpmPackageResolver;
use crate::npm::RealNpmRegistryApi; use crate::npm::RealNpmRegistryApi;
@ -235,17 +237,43 @@ impl LanguageServer {
} }
} }
fn create_lsp_npm_resolver(
dir: &DenoDir,
http_client: HttpClient,
) -> NpmPackageResolver {
let registry_url = RealNpmRegistryApi::default_url();
// Use an "only" cache setting in order to make the
// user do an explicit "cache" command and prevent
// the cache from being filled with lots of packages while
// the user is typing.
let cache_setting = CacheSetting::Only;
let progress_bar = ProgressBar::default();
let npm_cache = NpmCache::from_deno_dir(
dir,
cache_setting.clone(),
http_client.clone(),
progress_bar.clone(),
);
let api = RealNpmRegistryApi::new(
registry_url,
npm_cache.clone(),
cache_setting,
http_client,
progress_bar,
);
NpmPackageResolver::new(npm_cache, api, false, None)
}
impl Inner { impl Inner {
fn new(client: Client) -> Self { fn new(client: Client) -> Self {
let maybe_custom_root = env::var("DENO_DIR").map(String::into).ok(); let maybe_custom_root = env::var("DENO_DIR").map(String::into).ok();
let dir = deno_dir::DenoDir::new(maybe_custom_root) let dir = deno_dir::DenoDir::new(maybe_custom_root)
.expect("could not access DENO_DIR"); .expect("could not access DENO_DIR");
let module_registries_location = dir.root.join(REGISTRIES_PATH); let module_registries_location = dir.root.join(REGISTRIES_PATH);
let module_registries = ModuleRegistry::new( let http_client = HttpClient::new(None, None).unwrap();
&module_registries_location, let module_registries =
ModuleRegistryOptions::default(), ModuleRegistry::new(&module_registries_location, http_client.clone())
) .unwrap();
.expect("could not create module registries");
let location = dir.root.join(CACHE_PATH); let location = dir.root.join(CACHE_PATH);
let documents = Documents::new(&location); let documents = Documents::new(&location);
let cache_metadata = cache::CacheMetadata::new(&location); let cache_metadata = cache::CacheMetadata::new(&location);
@ -258,25 +286,7 @@ impl Inner {
ts_server.clone(), ts_server.clone(),
); );
let assets = Assets::new(ts_server.clone()); let assets = Assets::new(ts_server.clone());
let registry_url = RealNpmRegistryApi::default_url(); let npm_resolver = create_lsp_npm_resolver(&dir, http_client);
// Use an "only" cache setting in order to make the
// user do an explicit "cache" command and prevent
// the cache from being filled with lots of packages while
// the user is typing.
let cache_setting = CacheSetting::Only;
let progress_bar = ProgressBar::default();
let npm_cache = NpmCache::from_deno_dir(
&dir,
cache_setting.clone(),
progress_bar.clone(),
);
let api = RealNpmRegistryApi::new(
registry_url,
npm_cache.clone(),
cache_setting,
progress_bar,
);
let npm_resolver = NpmPackageResolver::new(npm_cache, api, false, None);
Self { Self {
assets, assets,
@ -498,33 +508,45 @@ impl Inner {
None None
}; };
if self.maybe_cache_path != maybe_cache_path { if self.maybe_cache_path != maybe_cache_path {
let maybe_custom_root = maybe_cache_path self.recreate_http_client_and_dependents(maybe_cache_path)?;
}
Ok(())
}
/// Recreates the http client and all dependent structs.
fn recreate_http_client_and_dependents(
&mut self,
new_cache_path: Option<PathBuf>,
) -> Result<(), AnyError> {
let maybe_custom_root = new_cache_path
.clone() .clone()
.or_else(|| env::var("DENO_DIR").map(String::into).ok()); .or_else(|| env::var("DENO_DIR").map(String::into).ok());
let dir = deno_dir::DenoDir::new(maybe_custom_root)?; let dir = deno_dir::DenoDir::new(maybe_custom_root)?;
let module_registries_location = dir.root.join(REGISTRIES_PATH);
let workspace_settings = self.config.get_workspace_settings(); let workspace_settings = self.config.get_workspace_settings();
let maybe_root_path = self let maybe_root_path = self
.config .config
.root_uri .root_uri
.as_ref() .as_ref()
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); .and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
self.module_registries = ModuleRegistry::new( let root_cert_store = Some(get_root_cert_store(
&module_registries_location,
ModuleRegistryOptions {
maybe_root_path, maybe_root_path,
maybe_ca_stores: workspace_settings.certificate_stores.clone(), workspace_settings.certificate_stores.clone(),
maybe_ca_file: workspace_settings.tls_certificate.clone(), workspace_settings.tls_certificate.clone(),
unsafely_ignore_certificate_errors: workspace_settings )?);
.unsafely_ignore_certificate_errors, let client = HttpClient::new(
}, root_cert_store,
workspace_settings.unsafely_ignore_certificate_errors,
)?; )?;
let module_registries_location = dir.root.join(REGISTRIES_PATH);
self.module_registries =
ModuleRegistry::new(&module_registries_location, client.clone())?;
self.module_registries_location = module_registries_location; self.module_registries_location = module_registries_location;
self.npm_resolver = create_lsp_npm_resolver(&dir, client);
// update the cache path
let location = dir.root.join(CACHE_PATH); let location = dir.root.join(CACHE_PATH);
self.documents.set_location(&location); self.documents.set_location(&location);
self.cache_metadata.set_location(&location); self.cache_metadata.set_location(&location);
self.maybe_cache_path = maybe_cache_path; self.maybe_cache_path = new_cache_path;
}
Ok(()) Ok(())
} }
@ -627,23 +649,8 @@ impl Inner {
async fn update_registries(&mut self) -> Result<(), AnyError> { async fn update_registries(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_registries", None::<()>); let mark = self.performance.mark("update_registries", None::<()>);
self.recreate_http_client_and_dependents(self.maybe_cache_path.clone())?;
let workspace_settings = self.config.get_workspace_settings(); let workspace_settings = self.config.get_workspace_settings();
let maybe_root_path = self
.config
.root_uri
.as_ref()
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
self.module_registries = ModuleRegistry::new(
&self.module_registries_location,
ModuleRegistryOptions {
maybe_root_path,
maybe_ca_stores: workspace_settings.certificate_stores.clone(),
maybe_ca_file: workspace_settings.tls_certificate.clone(),
unsafely_ignore_certificate_errors: workspace_settings
.unsafely_ignore_certificate_errors
.clone(),
},
)?;
for (registry, enabled) in workspace_settings.suggest.imports.hosts.iter() { for (registry, enabled) in workspace_settings.suggest.imports.hosts.iter() {
if *enabled { if *enabled {
lsp_log!("Enabling import suggestions for: {}", registry); lsp_log!("Enabling import suggestions for: {}", registry);

View file

@ -13,10 +13,10 @@ use super::path_to_regex::StringOrVec;
use super::path_to_regex::Token; use super::path_to_regex::Token;
use crate::deno_dir; use crate::deno_dir;
use crate::file_fetcher::get_root_cert_store;
use crate::file_fetcher::CacheSetting; use crate::file_fetcher::CacheSetting;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
use crate::http_cache::HttpCache; use crate::http_cache::HttpCache;
use crate::http_util::HttpClient;
use deno_core::anyhow::anyhow; use deno_core::anyhow::anyhow;
use deno_core::error::AnyError; use deno_core::error::AnyError;
@ -36,7 +36,6 @@ use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::path::PathBuf;
use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types as lsp;
const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json"; const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json";
@ -409,14 +408,6 @@ enum VariableItems {
List(VariableItemsList), List(VariableItemsList),
} }
#[derive(Debug, Default)]
pub struct ModuleRegistryOptions {
pub maybe_root_path: Option<PathBuf>,
pub maybe_ca_stores: Option<Vec<String>>,
pub maybe_ca_file: Option<String>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
}
/// A structure which holds the information about currently configured module /// A structure which holds the information about currently configured module
/// registries and can provide completion information for URLs that match /// registries and can provide completion information for URLs that match
/// one of the enabled registries. /// one of the enabled registries.
@ -433,28 +424,23 @@ impl Default for ModuleRegistry {
// custom root. // custom root.
let dir = deno_dir::DenoDir::new(None).unwrap(); let dir = deno_dir::DenoDir::new(None).unwrap();
let location = dir.root.join("registries"); let location = dir.root.join("registries");
Self::new(&location, ModuleRegistryOptions::default()).unwrap() let http_client = HttpClient::new(None, None).unwrap();
Self::new(&location, http_client).unwrap()
} }
} }
impl ModuleRegistry { impl ModuleRegistry {
pub fn new( pub fn new(
location: &Path, location: &Path,
options: ModuleRegistryOptions, http_client: HttpClient,
) -> Result<Self, AnyError> { ) -> Result<Self, AnyError> {
let http_cache = HttpCache::new(location); let http_cache = HttpCache::new(location);
let root_cert_store = Some(get_root_cert_store(
options.maybe_root_path,
options.maybe_ca_stores,
options.maybe_ca_file,
)?);
let mut file_fetcher = FileFetcher::new( let mut file_fetcher = FileFetcher::new(
http_cache, http_cache,
CacheSetting::RespectHeaders, CacheSetting::RespectHeaders,
true, true,
root_cert_store, http_client,
BlobStore::default(), BlobStore::default(),
options.unsafely_ignore_certificate_errors,
None, None,
)?; )?;
file_fetcher.set_download_log_level(super::logging::lsp_log_level()); file_fetcher.set_download_log_level(super::logging::lsp_log_level());
@ -1262,7 +1248,8 @@ mod tests {
let temp_dir = TempDir::new(); let temp_dir = TempDir::new();
let location = temp_dir.path().join("registries"); let location = temp_dir.path().join("registries");
let mut module_registry = let mut module_registry =
ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); ModuleRegistry::new(&location, HttpClient::new(None, None).unwrap())
.unwrap();
module_registry module_registry
.enable("http://localhost:4545/") .enable("http://localhost:4545/")
.await .await
@ -1323,7 +1310,8 @@ mod tests {
let temp_dir = TempDir::new(); let temp_dir = TempDir::new();
let location = temp_dir.path().join("registries"); let location = temp_dir.path().join("registries");
let mut module_registry = let mut module_registry =
ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); ModuleRegistry::new(&location, HttpClient::new(None, None).unwrap())
.unwrap();
module_registry module_registry
.enable("http://localhost:4545/") .enable("http://localhost:4545/")
.await .await
@ -1546,7 +1534,8 @@ mod tests {
let temp_dir = TempDir::new(); let temp_dir = TempDir::new();
let location = temp_dir.path().join("registries"); let location = temp_dir.path().join("registries");
let mut module_registry = let mut module_registry =
ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); ModuleRegistry::new(&location, HttpClient::new(None, None).unwrap())
.unwrap();
module_registry module_registry
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json") .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json")
.await .await
@ -1616,7 +1605,8 @@ mod tests {
let temp_dir = TempDir::new(); let temp_dir = TempDir::new();
let location = temp_dir.path().join("registries"); let location = temp_dir.path().join("registries");
let mut module_registry = let mut module_registry =
ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); ModuleRegistry::new(&location, HttpClient::new(None, None).unwrap())
.unwrap();
module_registry module_registry
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json") .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json")
.await .await
@ -1667,7 +1657,8 @@ mod tests {
let temp_dir = TempDir::new(); let temp_dir = TempDir::new();
let location = temp_dir.path().join("registries"); let location = temp_dir.path().join("registries");
let module_registry = let module_registry =
ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); ModuleRegistry::new(&location, HttpClient::new(None, None).unwrap())
.unwrap();
let result = module_registry.check_origin("http://localhost:4545").await; let result = module_registry.check_origin("http://localhost:4545").await;
assert!(result.is_ok()); assert!(result.is_ok());
} }
@ -1678,7 +1669,8 @@ mod tests {
let temp_dir = TempDir::new(); let temp_dir = TempDir::new();
let location = temp_dir.path().join("registries"); let location = temp_dir.path().join("registries");
let module_registry = let module_registry =
ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); ModuleRegistry::new(&location, HttpClient::new(None, None).unwrap())
.unwrap();
let result = module_registry.check_origin("https://deno.com").await; let result = module_registry.check_origin("https://deno.com").await;
assert!(result.is_err()); assert!(result.is_err());
let err = result.unwrap_err().to_string(); let err = result.unwrap_err().to_string();

View file

@ -10,11 +10,11 @@ use deno_core::anyhow::Context;
use deno_core::error::custom_error; use deno_core::error::custom_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::deno_fetch::reqwest;
use crate::deno_dir::DenoDir; use crate::deno_dir::DenoDir;
use crate::file_fetcher::CacheSetting; use crate::file_fetcher::CacheSetting;
use crate::fs_util; use crate::fs_util;
use crate::http_util::HttpClient;
use crate::progress_bar::ProgressBar; use crate::progress_bar::ProgressBar;
use super::registry::NpmPackageVersionDistInfo; use super::registry::NpmPackageVersionDistInfo;
@ -315,6 +315,7 @@ impl ReadonlyNpmCache {
pub struct NpmCache { pub struct NpmCache {
readonly: ReadonlyNpmCache, readonly: ReadonlyNpmCache,
cache_setting: CacheSetting, cache_setting: CacheSetting,
http_client: HttpClient,
progress_bar: ProgressBar, progress_bar: ProgressBar,
} }
@ -322,11 +323,13 @@ impl NpmCache {
pub fn from_deno_dir( pub fn from_deno_dir(
dir: &DenoDir, dir: &DenoDir,
cache_setting: CacheSetting, cache_setting: CacheSetting,
http_client: HttpClient,
progress_bar: ProgressBar, progress_bar: ProgressBar,
) -> Self { ) -> Self {
Self { Self {
readonly: ReadonlyNpmCache::from_deno_dir(dir), readonly: ReadonlyNpmCache::from_deno_dir(dir),
cache_setting, cache_setting,
http_client,
progress_bar, progress_bar,
} }
} }
@ -383,7 +386,7 @@ impl NpmCache {
} }
let _guard = self.progress_bar.update(&dist.tarball); let _guard = self.progress_bar.update(&dist.tarball);
let response = reqwest::get(&dist.tarball).await?; let response = self.http_client.get(&dist.tarball).send().await?;
if response.status() == 404 { if response.status() == 404 {
bail!("Could not find npm package tarball at: {}", dist.tarball); bail!("Could not find npm package tarball at: {}", dist.tarball);

View file

@ -18,12 +18,12 @@ use deno_core::serde::Deserialize;
use deno_core::serde_json; use deno_core::serde_json;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::colors; use deno_runtime::colors;
use deno_runtime::deno_fetch::reqwest;
use serde::Serialize; use serde::Serialize;
use crate::file_fetcher::CacheSetting; use crate::file_fetcher::CacheSetting;
use crate::fs_util; use crate::fs_util;
use crate::http_cache::CACHE_PERM; use crate::http_cache::CACHE_PERM;
use crate::http_util::HttpClient;
use crate::progress_bar::ProgressBar; use crate::progress_bar::ProgressBar;
use super::cache::NpmCache; use super::cache::NpmCache;
@ -249,6 +249,7 @@ impl RealNpmRegistryApi {
base_url: Url, base_url: Url,
cache: NpmCache, cache: NpmCache,
cache_setting: CacheSetting, cache_setting: CacheSetting,
http_client: HttpClient,
progress_bar: ProgressBar, progress_bar: ProgressBar,
) -> Self { ) -> Self {
Self(Arc::new(RealNpmRegistryApiInner { Self(Arc::new(RealNpmRegistryApiInner {
@ -256,6 +257,7 @@ impl RealNpmRegistryApi {
cache, cache,
mem_cache: Default::default(), mem_cache: Default::default(),
cache_setting, cache_setting,
http_client,
progress_bar, progress_bar,
})) }))
} }
@ -285,6 +287,7 @@ struct RealNpmRegistryApiInner {
cache: NpmCache, cache: NpmCache,
mem_cache: Mutex<HashMap<String, Option<Arc<NpmPackageInfo>>>>, mem_cache: Mutex<HashMap<String, Option<Arc<NpmPackageInfo>>>>,
cache_setting: CacheSetting, cache_setting: CacheSetting,
http_client: HttpClient,
progress_bar: ProgressBar, progress_bar: ProgressBar,
} }
@ -420,7 +423,7 @@ impl RealNpmRegistryApiInner {
let package_url = self.get_package_url(name); let package_url = self.get_package_url(name);
let _guard = self.progress_bar.update(package_url.as_str()); let _guard = self.progress_bar.update(package_url.as_str());
let response = match reqwest::get(package_url).await { let response = match self.http_client.get(package_url).send().await {
Ok(response) => response, Ok(response) => response,
Err(err) => { Err(err) => {
// attempt to use the local cache // attempt to use the local cache

View file

@ -19,6 +19,7 @@ use crate::graph_util::graph_lock_or_exit;
use crate::graph_util::GraphData; use crate::graph_util::GraphData;
use crate::graph_util::ModuleEntry; use crate::graph_util::ModuleEntry;
use crate::http_cache; use crate::http_cache;
use crate::http_util::HttpClient;
use crate::lockfile::as_maybe_locker; use crate::lockfile::as_maybe_locker;
use crate::lockfile::Lockfile; use crate::lockfile::Lockfile;
use crate::node; use crate::node;
@ -157,15 +158,18 @@ impl ProcState {
let root_cert_store = cli_options.resolve_root_cert_store()?; let root_cert_store = cli_options.resolve_root_cert_store()?;
let cache_usage = cli_options.cache_setting(); let cache_usage = cli_options.cache_setting();
let progress_bar = ProgressBar::default(); let progress_bar = ProgressBar::default();
let http_client = HttpClient::new(
Some(root_cert_store.clone()),
cli_options
.unsafely_ignore_certificate_errors()
.map(ToOwned::to_owned),
)?;
let file_fetcher = FileFetcher::new( let file_fetcher = FileFetcher::new(
http_cache, http_cache,
cache_usage, cache_usage,
!cli_options.no_remote(), !cli_options.no_remote(),
Some(root_cert_store.clone()), http_client.clone(),
blob_store.clone(), blob_store.clone(),
cli_options
.unsafely_ignore_certificate_errors()
.map(ToOwned::to_owned),
Some(progress_bar.clone()), Some(progress_bar.clone()),
)?; )?;
@ -216,12 +220,14 @@ impl ProcState {
let npm_cache = NpmCache::from_deno_dir( let npm_cache = NpmCache::from_deno_dir(
&dir, &dir,
cli_options.cache_setting(), cli_options.cache_setting(),
http_client.clone(),
progress_bar.clone(), progress_bar.clone(),
); );
let api = RealNpmRegistryApi::new( let api = RealNpmRegistryApi::new(
registry_url, registry_url,
npm_cache.clone(), npm_cache.clone(),
cli_options.cache_setting(), cli_options.cache_setting(),
http_client,
progress_bar.clone(), progress_bar.clone(),
); );
let maybe_lockfile = lockfile.as_ref().cloned(); let maybe_lockfile = lockfile.as_ref().cloned();