1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-12 00:54:02 -05:00

perf: lazily create RootCertStore (#18938)

This commit is contained in:
David Sherret 2023-05-01 16:42:05 -04:00 committed by GitHub
parent ecc70eb58f
commit 913176313b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 344 additions and 173 deletions

View file

@ -13,6 +13,7 @@ use self::package_json::PackageJsonDeps;
use ::import_map::ImportMap; use ::import_map::ImportMap;
use deno_core::resolve_url_or_path; use deno_core::resolve_url_or_path;
use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot; use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
use indexmap::IndexMap; use indexmap::IndexMap;
@ -52,6 +53,7 @@ use deno_runtime::deno_tls::webpki_roots;
use deno_runtime::inspector_server::InspectorServer; use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::PermissionsOptions; use deno_runtime::permissions::PermissionsOptions;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use once_cell::sync::OnceCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::io::BufReader; use std::io::BufReader;
@ -61,6 +63,7 @@ use std::num::NonZeroUsize;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc; use std::sync::Arc;
use thiserror::Error;
use crate::cache::DenoDir; use crate::cache::DenoDir;
use crate::file_fetcher::FileFetcher; use crate::file_fetcher::FileFetcher;
@ -401,13 +404,62 @@ fn discover_package_json(
Ok(None) Ok(None)
} }
struct CliRootCertStoreProvider {
cell: OnceCell<RootCertStore>,
maybe_root_path: Option<PathBuf>,
maybe_ca_stores: Option<Vec<String>>,
maybe_ca_data: Option<CaData>,
}
impl CliRootCertStoreProvider {
pub fn new(
maybe_root_path: Option<PathBuf>,
maybe_ca_stores: Option<Vec<String>>,
maybe_ca_data: Option<CaData>,
) -> Self {
Self {
cell: Default::default(),
maybe_root_path,
maybe_ca_stores,
maybe_ca_data,
}
}
}
impl RootCertStoreProvider for CliRootCertStoreProvider {
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> {
self
.cell
.get_or_try_init(|| {
get_root_cert_store(
self.maybe_root_path.clone(),
self.maybe_ca_stores.clone(),
self.maybe_ca_data.clone(),
)
})
.map_err(|e| e.into())
}
}
#[derive(Error, Debug, Clone)]
pub enum RootCertStoreLoadError {
#[error(
"Unknown certificate store \"{0}\" specified (allowed: \"system,mozilla\")"
)]
UnknownStore(String),
#[error("Unable to add pem file to certificate store: {0}")]
FailedAddPemFile(String),
#[error("Failed opening CA file: {0}")]
CaFileOpenError(String),
}
/// Create and populate a root cert store based on the passed options and /// Create and populate a root cert store based on the passed options and
/// environment. /// environment.
pub fn get_root_cert_store( pub fn get_root_cert_store(
maybe_root_path: Option<PathBuf>, maybe_root_path: Option<PathBuf>,
maybe_ca_stores: Option<Vec<String>>, maybe_ca_stores: Option<Vec<String>>,
maybe_ca_data: Option<CaData>, maybe_ca_data: Option<CaData>,
) -> Result<RootCertStore, AnyError> { ) -> Result<RootCertStore, RootCertStoreLoadError> {
let mut root_cert_store = RootCertStore::empty(); let mut root_cert_store = RootCertStore::empty();
let ca_stores: Vec<String> = maybe_ca_stores let ca_stores: Vec<String> = maybe_ca_stores
.or_else(|| { .or_else(|| {
@ -444,7 +496,7 @@ pub fn get_root_cert_store(
} }
} }
_ => { _ => {
return Err(anyhow!("Unknown certificate store \"{}\" specified (allowed: \"system,mozilla\")", store)); return Err(RootCertStoreLoadError::UnknownStore(store.clone()));
} }
} }
} }
@ -459,7 +511,9 @@ pub fn get_root_cert_store(
} else { } else {
PathBuf::from(ca_file) PathBuf::from(ca_file)
}; };
let certfile = std::fs::File::open(ca_file)?; let certfile = std::fs::File::open(ca_file).map_err(|err| {
RootCertStoreLoadError::CaFileOpenError(err.to_string())
})?;
let mut reader = BufReader::new(certfile); let mut reader = BufReader::new(certfile);
rustls_pemfile::certs(&mut reader) rustls_pemfile::certs(&mut reader)
} }
@ -474,10 +528,7 @@ pub fn get_root_cert_store(
root_cert_store.add_parsable_certificates(&certs); root_cert_store.add_parsable_certificates(&certs);
} }
Err(e) => { Err(e) => {
return Err(anyhow!( return Err(RootCertStoreLoadError::FailedAddPemFile(e.to_string()));
"Unable to add pem file to certificate store: {}",
e
));
} }
} }
} }
@ -799,12 +850,14 @@ impl CliOptions {
.map(|path| ModuleSpecifier::from_directory_path(path).unwrap()) .map(|path| ModuleSpecifier::from_directory_path(path).unwrap())
} }
pub fn resolve_root_cert_store(&self) -> Result<RootCertStore, AnyError> { pub fn resolve_root_cert_store_provider(
get_root_cert_store( &self,
) -> Arc<dyn RootCertStoreProvider> {
Arc::new(CliRootCertStoreProvider::new(
None, None,
self.flags.ca_stores.clone(), self.flags.ca_stores.clone(),
self.flags.ca_data.clone(), self.flags.ca_data.clone(),
) ))
} }
pub fn resolve_ts_config_for_emit( pub fn resolve_ts_config_for_emit(

View file

@ -45,7 +45,7 @@ use deno_core::parking_lot::Mutex;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::deno_node::analyze::NodeCodeTranslator; use deno_runtime::deno_node::analyze::NodeCodeTranslator;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
use deno_runtime::inspector_server::InspectorServer; use deno_runtime::inspector_server::InspectorServer;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
@ -129,14 +129,14 @@ struct CliFactoryServices {
dir: Deferred<DenoDir>, dir: Deferred<DenoDir>,
caches: Deferred<Arc<Caches>>, caches: Deferred<Arc<Caches>>,
file_fetcher: Deferred<Arc<FileFetcher>>, file_fetcher: Deferred<Arc<FileFetcher>>,
http_client: Deferred<HttpClient>, http_client: Deferred<Arc<HttpClient>>,
emit_cache: Deferred<EmitCache>, emit_cache: Deferred<EmitCache>,
emitter: Deferred<Arc<Emitter>>, emitter: Deferred<Arc<Emitter>>,
graph_container: Deferred<Arc<ModuleGraphContainer>>, graph_container: Deferred<Arc<ModuleGraphContainer>>,
lockfile: Deferred<Option<Arc<Mutex<Lockfile>>>>, lockfile: Deferred<Option<Arc<Mutex<Lockfile>>>>,
maybe_import_map: Deferred<Option<Arc<ImportMap>>>, maybe_import_map: Deferred<Option<Arc<ImportMap>>>,
maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>, maybe_inspector_server: Deferred<Option<Arc<InspectorServer>>>,
root_cert_store: Deferred<RootCertStore>, root_cert_store_provider: Deferred<Arc<dyn RootCertStoreProvider>>,
blob_store: Deferred<BlobStore>, blob_store: Deferred<BlobStore>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>, parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
resolver: Deferred<Arc<CliGraphResolver>>, resolver: Deferred<Arc<CliGraphResolver>>,
@ -208,11 +208,11 @@ impl CliFactory {
self.services.blob_store.get_or_init(BlobStore::default) self.services.blob_store.get_or_init(BlobStore::default)
} }
pub fn root_cert_store(&self) -> Result<&RootCertStore, AnyError> { pub fn root_cert_store_provider(&self) -> &Arc<dyn RootCertStoreProvider> {
self self
.services .services
.root_cert_store .root_cert_store_provider
.get_or_try_init(|| self.options.resolve_root_cert_store()) .get_or_init(|| self.options.resolve_root_cert_store_provider())
} }
pub fn text_only_progress_bar(&self) -> &ProgressBar { pub fn text_only_progress_bar(&self) -> &ProgressBar {
@ -222,12 +222,12 @@ impl CliFactory {
.get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly)) .get_or_init(|| ProgressBar::new(ProgressBarStyle::TextOnly))
} }
pub fn http_client(&self) -> Result<&HttpClient, AnyError> { pub fn http_client(&self) -> &Arc<HttpClient> {
self.services.http_client.get_or_try_init(|| { self.services.http_client.get_or_init(|| {
HttpClient::new( Arc::new(HttpClient::new(
Some(self.root_cert_store()?.clone()), Some(self.root_cert_store_provider().clone()),
self.options.unsafely_ignore_certificate_errors().clone(), self.options.unsafely_ignore_certificate_errors().clone(),
) ))
}) })
} }
@ -237,7 +237,7 @@ impl CliFactory {
HttpCache::new(&self.deno_dir()?.deps_folder_path()), HttpCache::new(&self.deno_dir()?.deps_folder_path()),
self.options.cache_setting(), self.options.cache_setting(),
!self.options.no_remote(), !self.options.no_remote(),
self.http_client()?.clone(), self.http_client().clone(),
self.blob_store().clone(), self.blob_store().clone(),
Some(self.text_only_progress_bar().clone()), Some(self.text_only_progress_bar().clone()),
))) )))
@ -256,7 +256,7 @@ impl CliFactory {
Ok(Arc::new(NpmCache::new( Ok(Arc::new(NpmCache::new(
self.deno_dir()?.npm_folder_path(), self.deno_dir()?.npm_folder_path(),
self.options.cache_setting(), self.options.cache_setting(),
self.http_client()?.clone(), self.http_client().clone(),
self.text_only_progress_bar().clone(), self.text_only_progress_bar().clone(),
))) )))
}) })
@ -267,7 +267,7 @@ impl CliFactory {
Ok(Arc::new(CliNpmRegistryApi::new( Ok(Arc::new(CliNpmRegistryApi::new(
CliNpmRegistryApi::default_url().to_owned(), CliNpmRegistryApi::default_url().to_owned(),
self.npm_cache()?.clone(), self.npm_cache()?.clone(),
self.http_client()?.clone(), self.http_client().clone(),
self.text_only_progress_bar().clone(), self.text_only_progress_bar().clone(),
))) )))
}) })
@ -554,7 +554,7 @@ impl CliFactory {
let options = self.cli_options().clone(); let options = self.cli_options().clone();
let main_worker_options = self.create_cli_main_worker_options()?; let main_worker_options = self.create_cli_main_worker_options()?;
let node_fs = self.node_fs().clone(); let node_fs = self.node_fs().clone();
let root_cert_store = self.root_cert_store()?.clone(); let root_cert_store_provider = self.root_cert_store_provider().clone();
let node_resolver = self.node_resolver().await?.clone(); let node_resolver = self.node_resolver().await?.clone();
let npm_resolver = self.npm_resolver().await?.clone(); let npm_resolver = self.npm_resolver().await?.clone();
let maybe_inspector_server = self.maybe_inspector_server().clone(); let maybe_inspector_server = self.maybe_inspector_server().clone();
@ -578,7 +578,7 @@ impl CliFactory {
node_resolver.clone(), node_resolver.clone(),
), ),
)), )),
root_cert_store.clone(), root_cert_store_provider.clone(),
node_fs.clone(), node_fs.clone(),
maybe_inspector_server.clone(), maybe_inspector_server.clone(),
main_worker_options.clone(), main_worker_options.clone(),
@ -609,7 +609,7 @@ impl CliFactory {
node_resolver.clone(), node_resolver.clone(),
), ),
)), )),
self.root_cert_store()?.clone(), self.root_cert_store_provider().clone(),
self.node_fs().clone(), self.node_fs().clone(),
self.maybe_inspector_server().clone(), self.maybe_inspector_server().clone(),
self.create_cli_main_worker_options()?, self.create_cli_main_worker_options()?,

View file

@ -178,7 +178,7 @@ pub struct FileFetcher {
cache: FileCache, cache: FileCache,
cache_setting: CacheSetting, cache_setting: CacheSetting,
pub http_cache: HttpCache, pub http_cache: HttpCache,
http_client: HttpClient, http_client: Arc<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>,
@ -189,7 +189,7 @@ impl FileFetcher {
http_cache: HttpCache, http_cache: HttpCache,
cache_setting: CacheSetting, cache_setting: CacheSetting,
allow_remote: bool, allow_remote: bool,
http_client: HttpClient, http_client: Arc<HttpClient>,
blob_store: BlobStore, blob_store: BlobStore,
progress_bar: Option<ProgressBar>, progress_bar: Option<ProgressBar>,
) -> Self { ) -> Self {
@ -660,7 +660,7 @@ async fn fetch_once<'a>(
http_client: &HttpClient, http_client: &HttpClient,
args: FetchOnceArgs<'a>, args: FetchOnceArgs<'a>,
) -> Result<FetchOnceResult, AnyError> { ) -> Result<FetchOnceResult, AnyError> {
let mut request = http_client.get_no_redirect(args.url.clone()); let mut request = http_client.get_no_redirect(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)?;
@ -769,7 +769,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
cache_setting, cache_setting,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
blob_store.clone(), blob_store.clone(),
None, None,
); );
@ -1207,7 +1207,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::ReloadAll, CacheSetting::ReloadAll,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -1232,7 +1232,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -1257,7 +1257,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -1398,7 +1398,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -1426,7 +1426,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -1525,7 +1525,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
false, false,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -1550,7 +1550,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Only, CacheSetting::Only,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -1558,7 +1558,7 @@ mod tests {
HttpCache::new(&location), HttpCache::new(&location),
CacheSetting::Use, CacheSetting::Use,
true, true,
HttpClient::new(None, None).unwrap(), Arc::new(HttpClient::new(None, None)),
BlobStore::default(), BlobStore::default(),
None, None,
); );
@ -2021,15 +2021,24 @@ mod tests {
#[ignore] // https://github.com/denoland/deno/issues/12561 #[ignore] // https://github.com/denoland/deno/issues/12561
async fn test_fetch_with_empty_certificate_store() { async fn test_fetch_with_empty_certificate_store() {
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
struct ValueRootCertStoreProvider(RootCertStore);
impl RootCertStoreProvider for ValueRootCertStoreProvider {
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> {
Ok(&self.0)
}
}
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 = HttpClient::new( let client = HttpClient::new(
Some(RootCertStore::empty()), // no certs loaded at all // no certs loaded at all
Some(Arc::new(ValueRootCertStoreProvider(RootCertStore::empty()))),
None, None,
) );
.unwrap();
let result = fetch_once( let result = fetch_once(
&client, &client,

View file

@ -15,8 +15,9 @@ use deno_runtime::deno_fetch::create_http_client;
use deno_runtime::deno_fetch::reqwest; use deno_runtime::deno_fetch::reqwest;
use deno_runtime::deno_fetch::reqwest::header::LOCATION; use deno_runtime::deno_fetch::reqwest::header::LOCATION;
use deno_runtime::deno_fetch::reqwest::Response; use deno_runtime::deno_fetch::reqwest::Response;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::RootCertStoreProvider;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use std::time::SystemTime; use std::time::SystemTime;
@ -217,34 +218,68 @@ impl CacheSemantics {
} }
} }
#[derive(Debug, Clone)] pub struct HttpClient {
pub struct HttpClient(reqwest::Client); root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
unsafely_ignore_certificate_errors: Option<Vec<String>>,
cell: once_cell::sync::OnceCell<reqwest::Client>,
}
impl std::fmt::Debug for HttpClient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpClient")
.field(
"unsafely_ignore_certificate_errors",
&self.unsafely_ignore_certificate_errors,
)
.finish()
}
}
impl HttpClient { impl HttpClient {
pub fn new( pub fn new(
root_cert_store: Option<RootCertStore>, root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
unsafely_ignore_certificate_errors: Option<Vec<String>>, unsafely_ignore_certificate_errors: Option<Vec<String>>,
) -> Result<Self, AnyError> { ) -> Self {
Ok(HttpClient::from_client(create_http_client( Self {
get_user_agent(), root_cert_store_provider,
root_cert_store,
vec![],
None,
unsafely_ignore_certificate_errors, unsafely_ignore_certificate_errors,
None, cell: Default::default(),
)?)) }
} }
#[cfg(test)]
pub fn from_client(client: reqwest::Client) -> Self { pub fn from_client(client: reqwest::Client) -> Self {
Self(client) let result = Self {
root_cert_store_provider: Default::default(),
unsafely_ignore_certificate_errors: Default::default(),
cell: Default::default(),
};
result.cell.set(client).unwrap();
result
}
fn client(&self) -> Result<&reqwest::Client, AnyError> {
self.cell.get_or_try_init(|| {
create_http_client(
get_user_agent(),
match &self.root_cert_store_provider {
Some(provider) => Some(provider.get_or_try_init()?.clone()),
None => None,
},
vec![],
None,
self.unsafely_ignore_certificate_errors.clone(),
None,
)
})
} }
/// Do a GET request without following redirects. /// Do a GET request without following redirects.
pub fn get_no_redirect<U: reqwest::IntoUrl>( pub fn get_no_redirect<U: reqwest::IntoUrl>(
&self, &self,
url: U, url: U,
) -> reqwest::RequestBuilder { ) -> Result<reqwest::RequestBuilder, AnyError> {
self.0.get(url) Ok(self.client()?.get(url))
} }
pub async fn download_text<U: reqwest::IntoUrl>( pub async fn download_text<U: reqwest::IntoUrl>(
@ -306,12 +341,13 @@ impl HttpClient {
url: U, url: U,
) -> Result<Response, AnyError> { ) -> Result<Response, AnyError> {
let mut url = url.into_url()?; let mut url = url.into_url()?;
let mut response = self.get_no_redirect(url.clone()).send().await?; let mut response = self.get_no_redirect(url.clone())?.send().await?;
let status = response.status(); let status = response.status();
if status.is_redirection() { if status.is_redirection() {
for _ in 0..5 { for _ in 0..5 {
let new_url = resolve_redirect_from_response(&url, &response)?; let new_url = resolve_redirect_from_response(&url, &response)?;
let new_response = self.get_no_redirect(new_url.clone()).send().await?; let new_response =
self.get_no_redirect(new_url.clone())?.send().await?;
let status = new_response.status(); let status = new_response.status();
if status.is_redirection() { if status.is_redirection() {
response = new_response; response = new_response;
@ -357,7 +393,7 @@ mod test {
#[tokio::test] #[tokio::test]
async fn test_http_client_download_redirect() { async fn test_http_client_download_redirect() {
let _http_server_guard = test_util::http_server(); let _http_server_guard = test_util::http_server();
let client = HttpClient::new(None, None).unwrap(); let client = HttpClient::new(None, None);
// make a request to the redirect server // make a request to the redirect server
let text = client let text = client

View file

@ -12,6 +12,8 @@ use deno_core::ModuleSpecifier;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
use import_map::ImportMap; use import_map::ImportMap;
use log::error; use log::error;
@ -93,6 +95,14 @@ use crate::util::path::specifier_to_file_path;
use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressBarStyle;
struct LspRootCertStoreProvider(RootCertStore);
impl RootCertStoreProvider for LspRootCertStoreProvider {
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> {
Ok(&self.0)
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LanguageServer(Arc<tokio::sync::RwLock<Inner>>); pub struct LanguageServer(Arc<tokio::sync::RwLock<Inner>>);
@ -124,7 +134,7 @@ pub struct Inner {
/// The collection of documents that the server is currently handling, either /// The collection of documents that the server is currently handling, either
/// on disk or "open" within the client. /// on disk or "open" within the client.
pub documents: Documents, pub documents: Documents,
http_client: HttpClient, http_client: Arc<HttpClient>,
/// Handles module registries, which allow discovery of modules /// Handles module registries, which allow discovery of modules
module_registries: ModuleRegistry, module_registries: ModuleRegistry,
/// The path to the module registries cache /// The path to the module registries cache
@ -420,7 +430,7 @@ impl LanguageServer {
fn create_lsp_structs( fn create_lsp_structs(
dir: &DenoDir, dir: &DenoDir,
http_client: HttpClient, http_client: Arc<HttpClient>,
) -> ( ) -> (
Arc<CliNpmRegistryApi>, Arc<CliNpmRegistryApi>,
Arc<NpmCache>, Arc<NpmCache>,
@ -469,10 +479,9 @@ impl Inner {
let dir = let dir =
DenoDir::new(maybe_custom_root).expect("could not access DENO_DIR"); DenoDir::new(maybe_custom_root).expect("could not access DENO_DIR");
let module_registries_location = dir.registries_folder_path(); let module_registries_location = dir.registries_folder_path();
let http_client = HttpClient::new(None, None).unwrap(); let http_client = Arc::new(HttpClient::new(None, None));
let module_registries = let module_registries =
ModuleRegistry::new(&module_registries_location, http_client.clone()) ModuleRegistry::new(&module_registries_location, http_client.clone());
.unwrap();
let location = dir.deps_folder_path(); let location = dir.deps_folder_path();
let documents = Documents::new(&location, client.kind()); let documents = Documents::new(&location, client.kind());
let deps_http_cache = HttpCache::new(&location); let deps_http_cache = HttpCache::new(&location);
@ -775,20 +784,22 @@ impl Inner {
.root_uri .root_uri
.as_ref() .as_ref()
.and_then(|uri| specifier_to_file_path(uri).ok()); .and_then(|uri| specifier_to_file_path(uri).ok());
let root_cert_store = Some(get_root_cert_store( let root_cert_store = get_root_cert_store(
maybe_root_path, maybe_root_path,
workspace_settings.certificate_stores, workspace_settings.certificate_stores,
workspace_settings.tls_certificate.map(CaData::File), workspace_settings.tls_certificate.map(CaData::File),
)?);
let module_registries_location = dir.registries_folder_path();
self.http_client = HttpClient::new(
root_cert_store,
workspace_settings.unsafely_ignore_certificate_errors,
)?; )?;
let root_cert_store_provider =
Arc::new(LspRootCertStoreProvider(root_cert_store));
let module_registries_location = dir.registries_folder_path();
self.http_client = Arc::new(HttpClient::new(
Some(root_cert_store_provider),
workspace_settings.unsafely_ignore_certificate_errors,
));
self.module_registries = ModuleRegistry::new( self.module_registries = ModuleRegistry::new(
&module_registries_location, &module_registries_location,
self.http_client.clone(), self.http_client.clone(),
)?; );
self.module_registries_location = module_registries_location; self.module_registries_location = module_registries_location;
( (
self.npm_api, self.npm_api,

View file

@ -35,6 +35,7 @@ use log::error;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
use std::sync::Arc;
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";
@ -425,16 +426,13 @@ impl Default for ModuleRegistry {
// custom root. // custom root.
let dir = DenoDir::new(None).unwrap(); let dir = DenoDir::new(None).unwrap();
let location = dir.registries_folder_path(); let location = dir.registries_folder_path();
let http_client = HttpClient::new(None, None).unwrap(); let http_client = Arc::new(HttpClient::new(None, None));
Self::new(&location, http_client).unwrap() Self::new(&location, http_client)
} }
} }
impl ModuleRegistry { impl ModuleRegistry {
pub fn new( pub fn new(location: &Path, http_client: Arc<HttpClient>) -> Self {
location: &Path,
http_client: HttpClient,
) -> Result<Self, AnyError> {
let http_cache = HttpCache::new(location); let http_cache = HttpCache::new(location);
let mut file_fetcher = FileFetcher::new( let mut file_fetcher = FileFetcher::new(
http_cache, http_cache,
@ -446,10 +444,10 @@ impl ModuleRegistry {
); );
file_fetcher.set_download_log_level(super::logging::lsp_log_level()); file_fetcher.set_download_log_level(super::logging::lsp_log_level());
Ok(Self { Self {
origins: HashMap::new(), origins: HashMap::new(),
file_fetcher, file_fetcher,
}) }
} }
fn complete_literal( fn complete_literal(
@ -1251,8 +1249,7 @@ 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, HttpClient::new(None, None).unwrap()) ModuleRegistry::new(&location, Arc::new(HttpClient::new(None, None)));
.unwrap();
module_registry module_registry
.enable("http://localhost:4545/") .enable("http://localhost:4545/")
.await .await
@ -1313,8 +1310,7 @@ 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, HttpClient::new(None, None).unwrap()) ModuleRegistry::new(&location, Arc::new(HttpClient::new(None, None)));
.unwrap();
module_registry module_registry
.enable("http://localhost:4545/") .enable("http://localhost:4545/")
.await .await
@ -1537,8 +1533,7 @@ 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, HttpClient::new(None, None).unwrap()) ModuleRegistry::new(&location, Arc::new(HttpClient::new(None, None)));
.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
@ -1608,8 +1603,7 @@ 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, HttpClient::new(None, None).unwrap()) ModuleRegistry::new(&location, Arc::new(HttpClient::new(None, None)));
.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
@ -1660,8 +1654,7 @@ 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, HttpClient::new(None, None).unwrap()) ModuleRegistry::new(&location, Arc::new(HttpClient::new(None, None)));
.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());
} }
@ -1672,8 +1665,7 @@ 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, HttpClient::new(None, None).unwrap()) ModuleRegistry::new(&location, Arc::new(HttpClient::new(None, None)));
.unwrap();
let result = module_registry.check_origin("https://example.com").await; let result = module_registry.check_origin("https://example.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

@ -4,6 +4,7 @@ use std::collections::HashSet;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::Arc;
use deno_ast::ModuleSpecifier; use deno_ast::ModuleSpecifier;
use deno_core::anyhow::bail; use deno_core::anyhow::bail;
@ -295,7 +296,7 @@ impl ReadonlyNpmCache {
pub struct NpmCache { pub struct NpmCache {
readonly: ReadonlyNpmCache, readonly: ReadonlyNpmCache,
cache_setting: CacheSetting, cache_setting: CacheSetting,
http_client: HttpClient, http_client: Arc<HttpClient>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
/// ensures a package is only downloaded once per run /// ensures a package is only downloaded once per run
previously_reloaded_packages: Mutex<HashSet<NpmPackageNv>>, previously_reloaded_packages: Mutex<HashSet<NpmPackageNv>>,
@ -305,7 +306,7 @@ impl NpmCache {
pub fn new( pub fn new(
cache_dir_path: PathBuf, cache_dir_path: PathBuf,
cache_setting: CacheSetting, cache_setting: CacheSetting,
http_client: HttpClient, http_client: Arc<HttpClient>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
) -> Self { ) -> Self {
Self { Self {

View file

@ -63,7 +63,7 @@ impl CliNpmRegistryApi {
pub fn new( pub fn new(
base_url: Url, base_url: Url,
cache: Arc<NpmCache>, cache: Arc<NpmCache>,
http_client: HttpClient, http_client: Arc<HttpClient>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
) -> Self { ) -> Self {
Self(Some(Arc::new(CliNpmRegistryApiInner { Self(Some(Arc::new(CliNpmRegistryApiInner {
@ -172,7 +172,7 @@ struct CliNpmRegistryApiInner {
force_reload_flag: AtomicFlag, force_reload_flag: AtomicFlag,
mem_cache: Mutex<HashMap<String, CacheItem>>, mem_cache: Mutex<HashMap<String, CacheItem>>,
previously_reloaded_packages: Mutex<HashSet<String>>, previously_reloaded_packages: Mutex<HashSet<String>>,
http_client: HttpClient, http_client: Arc<HttpClient>,
progress_bar: ProgressBar, progress_bar: ProgressBar,
} }

View file

@ -32,6 +32,8 @@ use deno_core::ResolutionKind;
use deno_graph::source::Resolver; use deno_graph::source::Resolver;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
use deno_runtime::permissions::Permissions; use deno_runtime::permissions::Permissions;
use deno_runtime::permissions::PermissionsContainer; use deno_runtime::permissions::PermissionsContainer;
@ -161,22 +163,37 @@ impl HasNodeSpecifierChecker for StandaloneHasNodeSpecifierChecker {
} }
} }
struct StandaloneRootCertStoreProvider {
ca_stores: Option<Vec<String>>,
ca_data: Option<CaData>,
cell: once_cell::sync::OnceCell<RootCertStore>,
}
impl RootCertStoreProvider for StandaloneRootCertStoreProvider {
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError> {
self.cell.get_or_try_init(|| {
get_root_cert_store(None, self.ca_stores.clone(), self.ca_data.clone())
.map_err(|err| err.into())
})
}
}
pub async fn run( pub async fn run(
eszip: eszip::EszipV2, eszip: eszip::EszipV2,
metadata: Metadata, metadata: Metadata,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let main_module = &metadata.entrypoint; let main_module = &metadata.entrypoint;
let dir = DenoDir::new(None)?; let dir = DenoDir::new(None)?;
let root_cert_store = get_root_cert_store( let root_cert_store_provider = Arc::new(StandaloneRootCertStoreProvider {
None, ca_stores: metadata.ca_stores,
metadata.ca_stores, ca_data: metadata.ca_data.map(CaData::Bytes),
metadata.ca_data.map(CaData::Bytes), cell: Default::default(),
)?; });
let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly); let progress_bar = ProgressBar::new(ProgressBarStyle::TextOnly);
let http_client = HttpClient::new( let http_client = Arc::new(HttpClient::new(
Some(root_cert_store.clone()), Some(root_cert_store_provider.clone()),
metadata.unsafely_ignore_certificate_errors.clone(), metadata.unsafely_ignore_certificate_errors.clone(),
)?; ));
let npm_registry_url = CliNpmRegistryApi::default_url().to_owned(); let npm_registry_url = CliNpmRegistryApi::default_url().to_owned();
let npm_cache = Arc::new(NpmCache::new( let npm_cache = Arc::new(NpmCache::new(
dir.npm_folder_path(), dir.npm_folder_path(),
@ -235,7 +252,7 @@ pub async fn run(
Box::new(StandaloneHasNodeSpecifierChecker), Box::new(StandaloneHasNodeSpecifierChecker),
BlobStore::default(), BlobStore::default(),
Box::new(module_loader_factory), Box::new(module_loader_factory),
root_cert_store, root_cert_store_provider,
node_fs, node_fs,
None, None,
CliMainWorkerOptions { CliMainWorkerOptions {

View file

@ -133,7 +133,7 @@ pub async fn infer_name_from_url(url: &Url) -> Option<String> {
let mut url = url.clone(); let mut url = url.clone();
if url.path() == "/" { if url.path() == "/" {
let client = HttpClient::new(None, None).unwrap(); let client = HttpClient::new(None, None);
if let Ok(res) = client.get_redirected_response(url.clone()).await { if let Ok(res) = client.get_redirected_response(url.clone()).await {
url = res.url().clone(); url = res.url().clone();
} }

View file

@ -35,7 +35,7 @@ To grant permissions, set them before the script argument. For example:
// map specified and bare specifier is used on the command line // map specified and bare specifier is used on the command line
let factory = CliFactory::from_flags(flags).await?; let factory = CliFactory::from_flags(flags).await?;
let deno_dir = factory.deno_dir()?; let deno_dir = factory.deno_dir()?;
let http_client = factory.http_client()?; let http_client = factory.http_client();
let cli_options = factory.cli_options(); let cli_options = factory.cli_options();
// Run a background task that checks for available upgrades. If an earlier // Run a background task that checks for available upgrades. If an earlier

View file

@ -26,7 +26,7 @@ pub async fn compile(
let factory = CliFactory::from_flags(flags).await?; let factory = CliFactory::from_flags(flags).await?;
let cli_options = factory.cli_options(); let cli_options = factory.cli_options();
let file_fetcher = factory.file_fetcher()?; let file_fetcher = factory.file_fetcher()?;
let http_client = factory.http_client()?; let http_client = factory.http_client();
let deno_dir = factory.deno_dir()?; let deno_dir = factory.deno_dir()?;
let module_graph_builder = factory.module_graph_builder().await?; let module_graph_builder = factory.module_graph_builder().await?;
let parsed_source_cache = factory.parsed_source_cache()?; let parsed_source_cache = factory.parsed_source_cache()?;

View file

@ -26,6 +26,7 @@ use std::ops::Sub;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
static ARCHIVE_NAME: Lazy<String> = static ARCHIVE_NAME: Lazy<String> =
@ -50,13 +51,13 @@ trait UpdateCheckerEnvironment: Clone + Send + Sync {
#[derive(Clone)] #[derive(Clone)]
struct RealUpdateCheckerEnvironment { struct RealUpdateCheckerEnvironment {
http_client: HttpClient, http_client: Arc<HttpClient>,
cache_file_path: PathBuf, cache_file_path: PathBuf,
current_time: chrono::DateTime<chrono::Utc>, current_time: chrono::DateTime<chrono::Utc>,
} }
impl RealUpdateCheckerEnvironment { impl RealUpdateCheckerEnvironment {
pub fn new(http_client: HttpClient, cache_file_path: PathBuf) -> Self { pub fn new(http_client: Arc<HttpClient>, cache_file_path: PathBuf) -> Self {
Self { Self {
http_client, http_client,
cache_file_path, cache_file_path,
@ -183,7 +184,10 @@ fn print_release_notes(current_version: &str, new_version: &str) {
} }
} }
pub fn check_for_upgrades(http_client: HttpClient, cache_file_path: PathBuf) { pub fn check_for_upgrades(
http_client: Arc<HttpClient>,
cache_file_path: PathBuf,
) {
if env::var("DENO_NO_UPDATE_CHECK").is_ok() { if env::var("DENO_NO_UPDATE_CHECK").is_ok() {
return; return;
} }
@ -264,7 +268,7 @@ pub async fn upgrade(
upgrade_flags: UpgradeFlags, upgrade_flags: UpgradeFlags,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
let factory = CliFactory::from_flags(flags).await?; let factory = CliFactory::from_flags(flags).await?;
let client = factory.http_client()?; let client = factory.http_client();
let current_exe_path = std::env::current_exe()?; let current_exe_path = std::env::current_exe()?;
let metadata = fs::metadata(&current_exe_path)?; let metadata = fs::metadata(&current_exe_path)?;
let permissions = metadata.permissions(); let permissions = metadata.permissions();

View file

@ -21,7 +21,7 @@ use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_node; use deno_runtime::deno_node;
use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_tls::rustls::RootCertStore; use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore; use deno_runtime::deno_web::BlobStore;
use deno_runtime::fmt_errors::format_js_error; use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::inspector_server::InspectorServer; use deno_runtime::inspector_server::InspectorServer;
@ -96,7 +96,7 @@ struct SharedWorkerState {
shared_array_buffer_store: SharedArrayBufferStore, shared_array_buffer_store: SharedArrayBufferStore,
compiled_wasm_module_store: CompiledWasmModuleStore, compiled_wasm_module_store: CompiledWasmModuleStore,
module_loader_factory: Box<dyn ModuleLoaderFactory>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
root_cert_store: RootCertStore, root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
node_fs: Arc<dyn deno_node::NodeFs>, node_fs: Arc<dyn deno_node::NodeFs>,
maybe_inspector_server: Option<Arc<InspectorServer>>, maybe_inspector_server: Option<Arc<InspectorServer>>,
} }
@ -307,7 +307,7 @@ impl CliMainWorkerFactory {
has_node_specifier_checker: Box<dyn HasNodeSpecifierChecker>, has_node_specifier_checker: Box<dyn HasNodeSpecifierChecker>,
blob_store: BlobStore, blob_store: BlobStore,
module_loader_factory: Box<dyn ModuleLoaderFactory>, module_loader_factory: Box<dyn ModuleLoaderFactory>,
root_cert_store: RootCertStore, root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
node_fs: Arc<dyn deno_node::NodeFs>, node_fs: Arc<dyn deno_node::NodeFs>,
maybe_inspector_server: Option<Arc<InspectorServer>>, maybe_inspector_server: Option<Arc<InspectorServer>>,
options: CliMainWorkerOptions, options: CliMainWorkerOptions,
@ -324,7 +324,7 @@ impl CliMainWorkerFactory {
shared_array_buffer_store: Default::default(), shared_array_buffer_store: Default::default(),
compiled_wasm_module_store: Default::default(), compiled_wasm_module_store: Default::default(),
module_loader_factory, module_loader_factory,
root_cert_store, root_cert_store_provider,
node_fs, node_fs,
maybe_inspector_server, maybe_inspector_server,
}), }),
@ -434,7 +434,7 @@ impl CliMainWorkerFactory {
.options .options
.unsafely_ignore_certificate_errors .unsafely_ignore_certificate_errors
.clone(), .clone(),
root_cert_store: Some(shared.root_cert_store.clone()), root_cert_store_provider: Some(shared.root_cert_store_provider.clone()),
seed: shared.options.seed, seed: shared.options.seed,
source_map_getter: maybe_source_map_getter, source_map_getter: maybe_source_map_getter,
format_js_error_fn: Some(Arc::new(format_js_error)), format_js_error_fn: Some(Arc::new(format_js_error)),
@ -562,7 +562,7 @@ fn create_web_worker_callback(
.options .options
.unsafely_ignore_certificate_errors .unsafely_ignore_certificate_errors
.clone(), .clone(),
root_cert_store: Some(shared.root_cert_store.clone()), root_cert_store_provider: Some(shared.root_cert_store_provider.clone()),
seed: shared.options.seed, seed: shared.options.seed,
create_web_worker_cb, create_web_worker_cb,
preload_module_cb, preload_module_cb,
@ -616,7 +616,7 @@ mod tests {
extensions: vec![], extensions: vec![],
startup_snapshot: Some(crate::js::deno_isolate_init()), startup_snapshot: Some(crate::js::deno_isolate_init()),
unsafely_ignore_certificate_errors: None, unsafely_ignore_certificate_errors: None,
root_cert_store: None, root_cert_store_provider: None,
seed: None, seed: None,
format_js_error_fn: None, format_js_error_fn: None,
source_map_getter: None, source_map_getter: None,

View file

@ -3,7 +3,16 @@
mod byte_stream; mod byte_stream;
mod fs_fetch_handler; mod fs_fetch_handler;
use data_url::DataUrl; use std::borrow::Cow;
use std::cell::RefCell;
use std::cmp::min;
use std::convert::From;
use std::path::Path;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use deno_core::error::type_error; use deno_core::error::type_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::futures::stream::Peekable; use deno_core::futures::stream::Peekable;
@ -29,6 +38,9 @@ use deno_core::ResourceId;
use deno_core::ZeroCopyBuf; use deno_core::ZeroCopyBuf;
use deno_tls::rustls::RootCertStore; use deno_tls::rustls::RootCertStore;
use deno_tls::Proxy; use deno_tls::Proxy;
use deno_tls::RootCertStoreProvider;
use data_url::DataUrl;
use http::header::CONTENT_LENGTH; use http::header::CONTENT_LENGTH;
use http::Uri; use http::Uri;
use reqwest::header::HeaderMap; use reqwest::header::HeaderMap;
@ -46,14 +58,6 @@ use reqwest::RequestBuilder;
use reqwest::Response; use reqwest::Response;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use std::borrow::Cow;
use std::cell::RefCell;
use std::cmp::min;
use std::convert::From;
use std::path::Path;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;
use tokio::sync::mpsc; use tokio::sync::mpsc;
// Re-export reqwest and data_url // Re-export reqwest and data_url
@ -67,7 +71,7 @@ use crate::byte_stream::MpscByteStream;
#[derive(Clone)] #[derive(Clone)]
pub struct Options { pub struct Options {
pub user_agent: String, pub user_agent: String,
pub root_cert_store: Option<RootCertStore>, pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
pub proxy: Option<Proxy>, pub proxy: Option<Proxy>,
pub request_builder_hook: pub request_builder_hook:
Option<fn(RequestBuilder) -> Result<RequestBuilder, AnyError>>, Option<fn(RequestBuilder) -> Result<RequestBuilder, AnyError>>,
@ -76,11 +80,20 @@ pub struct Options {
pub file_fetch_handler: Rc<dyn FetchHandler>, pub file_fetch_handler: Rc<dyn FetchHandler>,
} }
impl Options {
pub fn root_cert_store(&self) -> Result<Option<RootCertStore>, AnyError> {
Ok(match &self.root_cert_store_provider {
Some(provider) => Some(provider.get_or_try_init()?.clone()),
None => None,
})
}
}
impl Default for Options { impl Default for Options {
fn default() -> Self { fn default() -> Self {
Self { Self {
user_agent: "".to_string(), user_agent: "".to_string(),
root_cert_store: None, root_cert_store_provider: None,
proxy: None, proxy: None,
request_builder_hook: None, request_builder_hook: None,
unsafely_ignore_certificate_errors: None, unsafely_ignore_certificate_errors: None,
@ -111,18 +124,7 @@ deno_core::extension!(deno_fetch,
options: Options, options: Options,
}, },
state = |state, options| { state = |state, options| {
state.put::<Options>(options.options.clone()); state.put::<Options>(options.options);
state.put::<reqwest::Client>({
create_http_client(
&options.options.user_agent,
options.options.root_cert_store,
vec![],
options.options.proxy,
options.options.unsafely_ignore_certificate_errors,
options.options.client_cert_chain_and_key
)
.unwrap()
});
}, },
); );
@ -189,6 +191,26 @@ pub struct FetchReturn {
cancel_handle_rid: Option<ResourceId>, cancel_handle_rid: Option<ResourceId>,
} }
pub fn get_or_create_client_from_state(
state: &mut OpState,
) -> Result<reqwest::Client, AnyError> {
if let Some(client) = state.try_borrow::<reqwest::Client>() {
Ok(client.clone())
} else {
let options = state.borrow::<Options>();
let client = create_http_client(
&options.user_agent,
options.root_cert_store()?,
vec![],
options.proxy.clone(),
options.unsafely_ignore_certificate_errors.clone(),
options.client_cert_chain_and_key.clone(),
)?;
state.put::<reqwest::Client>(client.clone());
Ok(client)
}
}
#[op] #[op]
pub fn op_fetch<FP>( pub fn op_fetch<FP>(
state: &mut OpState, state: &mut OpState,
@ -207,8 +229,7 @@ where
let r = state.resource_table.get::<HttpClientResource>(rid)?; let r = state.resource_table.get::<HttpClientResource>(rid)?;
r.client.clone() r.client.clone()
} else { } else {
let client = state.borrow::<reqwest::Client>(); get_or_create_client_from_state(state)?
client.clone()
}; };
let method = Method::from_bytes(&method)?; let method = Method::from_bytes(&method)?;
@ -632,7 +653,7 @@ where
let client = create_http_client( let client = create_http_client(
&options.user_agent, &options.user_agent,
options.root_cert_store.clone(), options.root_cert_store()?,
ca_certs, ca_certs,
args.proxy, args.proxy,
options.unsafely_ignore_certificate_errors.clone(), options.unsafely_ignore_certificate_errors.clone(),

View file

@ -11,10 +11,12 @@ pub mod resolve_addr;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::OpState; use deno_core::OpState;
use deno_tls::rustls::RootCertStore; use deno_tls::rustls::RootCertStore;
use deno_tls::RootCertStoreProvider;
use std::cell::RefCell; use std::cell::RefCell;
use std::path::Path; use std::path::Path;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
pub trait NetPermissions { pub trait NetPermissions {
fn check_net<T: AsRef<str>>( fn check_net<T: AsRef<str>>(
@ -67,7 +69,16 @@ pub fn get_declaration() -> PathBuf {
#[derive(Clone)] #[derive(Clone)]
pub struct DefaultTlsOptions { pub struct DefaultTlsOptions {
pub root_cert_store: Option<RootCertStore>, pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
}
impl DefaultTlsOptions {
pub fn root_cert_store(&self) -> Result<Option<RootCertStore>, AnyError> {
Ok(match &self.root_cert_store_provider {
Some(provider) => Some(provider.get_or_try_init()?.clone()),
None => None,
})
}
} }
/// `UnsafelyIgnoreCertificateErrors` is a wrapper struct so it can be placed inside `GothamState`; /// `UnsafelyIgnoreCertificateErrors` is a wrapper struct so it can be placed inside `GothamState`;
@ -113,13 +124,13 @@ deno_core::extension!(deno_net,
], ],
esm = [ "01_net.js", "02_tls.js" ], esm = [ "01_net.js", "02_tls.js" ],
options = { options = {
root_cert_store: Option<RootCertStore>, root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
unstable: bool, unstable: bool,
unsafely_ignore_certificate_errors: Option<Vec<String>>, unsafely_ignore_certificate_errors: Option<Vec<String>>,
}, },
state = |state, options| { state = |state, options| {
state.put(DefaultTlsOptions { state.put(DefaultTlsOptions {
root_cert_store: options.root_cert_store, root_cert_store_provider: options.root_cert_store_provider,
}); });
state.put(UnstableChecker { unstable: options.unstable }); state.put(UnstableChecker { unstable: options.unstable });
state.put(UnsafelyIgnoreCertificateErrors( state.put(UnsafelyIgnoreCertificateErrors(

View file

@ -813,14 +813,10 @@ where
.try_borrow::<UnsafelyIgnoreCertificateErrors>() .try_borrow::<UnsafelyIgnoreCertificateErrors>()
.and_then(|it| it.0.clone()); .and_then(|it| it.0.clone());
// TODO(@justinmchase): Ideally the certificate store is created once
// and not cloned. The store should be wrapped in Arc<T> to reduce
// copying memory unnecessarily.
let root_cert_store = state let root_cert_store = state
.borrow() .borrow()
.borrow::<DefaultTlsOptions>() .borrow::<DefaultTlsOptions>()
.root_cert_store .root_cert_store()?;
.clone();
let resource_rc = state let resource_rc = state
.borrow_mut() .borrow_mut()
@ -912,8 +908,7 @@ where
let root_cert_store = state let root_cert_store = state
.borrow() .borrow()
.borrow::<DefaultTlsOptions>() .borrow::<DefaultTlsOptions>()
.root_cert_store .root_cert_store()?;
.clone();
let hostname_dns = ServerName::try_from(&*addr.hostname) let hostname_dns = ServerName::try_from(&*addr.hostname)
.map_err(|_| invalid_hostname(&addr.hostname))?; .map_err(|_| invalid_hostname(&addr.hostname))?;
let connect_addr = resolve_addr(&addr.hostname, addr.port) let connect_addr = resolve_addr(&addr.hostname, addr.port)

View file

@ -34,6 +34,14 @@ use std::io::Cursor;
use std::sync::Arc; use std::sync::Arc;
use std::time::SystemTime; use std::time::SystemTime;
/// Lazily resolves the root cert store.
///
/// This was done because the root cert store is not needed in all cases
/// and takes a bit of time to initialize.
pub trait RootCertStoreProvider: Send + Sync {
fn get_or_try_init(&self) -> Result<&RootCertStore, AnyError>;
}
// This extension has no runtime apis, it only exports some shared native functions. // This extension has no runtime apis, it only exports some shared native functions.
deno_core::extension!(deno_tls); deno_core::extension!(deno_tls);

View file

@ -19,6 +19,7 @@ use deno_core::ZeroCopyBuf;
use deno_net::raw::take_network_stream_resource; use deno_net::raw::take_network_stream_resource;
use deno_net::raw::NetworkStream; use deno_net::raw::NetworkStream;
use deno_tls::create_client_config; use deno_tls::create_client_config;
use deno_tls::RootCertStoreProvider;
use http::header::CONNECTION; use http::header::CONNECTION;
use http::header::UPGRADE; use http::header::UPGRADE;
use http::HeaderName; use http::HeaderName;
@ -54,7 +55,17 @@ use fastwebsockets::WebSocket;
mod stream; mod stream;
#[derive(Clone)] #[derive(Clone)]
pub struct WsRootStore(pub Option<RootCertStore>); pub struct WsRootStoreProvider(Option<Arc<dyn RootCertStoreProvider>>);
impl WsRootStoreProvider {
pub fn get_or_try_init(&self) -> Result<Option<RootCertStore>, AnyError> {
Ok(match &self.0 {
Some(provider) => Some(provider.get_or_try_init()?.clone()),
None => None,
})
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct WsUserAgent(pub String); pub struct WsUserAgent(pub String);
@ -181,7 +192,10 @@ where
.borrow() .borrow()
.try_borrow::<UnsafelyIgnoreCertificateErrors>() .try_borrow::<UnsafelyIgnoreCertificateErrors>()
.and_then(|it| it.0.clone()); .and_then(|it| it.0.clone());
let root_cert_store = state.borrow().borrow::<WsRootStore>().0.clone(); let root_cert_store = state
.borrow()
.borrow::<WsRootStoreProvider>()
.get_or_try_init()?;
let user_agent = state.borrow().borrow::<WsUserAgent>().0.clone(); let user_agent = state.borrow().borrow::<WsUserAgent>().0.clone();
let uri: Uri = url.parse()?; let uri: Uri = url.parse()?;
let mut request = Request::builder().method(Method::GET).uri( let mut request = Request::builder().method(Method::GET).uri(
@ -525,7 +539,7 @@ deno_core::extension!(deno_websocket,
esm = [ "01_websocket.js", "02_websocketstream.js" ], esm = [ "01_websocket.js", "02_websocketstream.js" ],
options = { options = {
user_agent: String, user_agent: String,
root_cert_store: Option<RootCertStore>, root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
unsafely_ignore_certificate_errors: Option<Vec<String>> unsafely_ignore_certificate_errors: Option<Vec<String>>
}, },
state = |state, options| { state = |state, options| {
@ -533,7 +547,7 @@ deno_core::extension!(deno_websocket,
state.put(UnsafelyIgnoreCertificateErrors( state.put(UnsafelyIgnoreCertificateErrors(
options.unsafely_ignore_certificate_errors, options.unsafely_ignore_certificate_errors,
)); ));
state.put::<WsRootStore>(WsRootStore(options.root_cert_store)); state.put::<WsRootStoreProvider>(WsRootStoreProvider(options.root_cert_store_provider));
}, },
); );

View file

@ -32,7 +32,7 @@ async fn main() -> Result<(), AnyError> {
extensions: vec![], extensions: vec![],
startup_snapshot: None, startup_snapshot: None,
unsafely_ignore_certificate_errors: None, unsafely_ignore_certificate_errors: None,
root_cert_store: None, root_cert_store_provider: None,
seed: None, seed: None,
source_map_getter: None, source_map_getter: None,
format_js_error_fn: None, format_js_error_fn: None,

View file

@ -8,7 +8,6 @@ use deno_core::op;
use deno_core::url::Url; use deno_core::url::Url;
use deno_core::OpState; use deno_core::OpState;
use deno_fetch::data_url::DataUrl; use deno_fetch::data_url::DataUrl;
use deno_fetch::reqwest;
use deno_web::BlobStore; use deno_web::BlobStore;
use deno_websocket::DomExceptionNetworkError; use deno_websocket::DomExceptionNetworkError;
use hyper::body::Bytes; use hyper::body::Bytes;
@ -41,7 +40,7 @@ pub fn op_worker_sync_fetch(
let handle = state.borrow::<WebWorkerInternalHandle>().clone(); let handle = state.borrow::<WebWorkerInternalHandle>().clone();
assert_eq!(handle.worker_type, WebWorkerType::Classic); assert_eq!(handle.worker_type, WebWorkerType::Classic);
let client = state.borrow::<reqwest::Client>().clone(); let client = deno_fetch::get_or_create_client_from_state(state)?;
// TODO(andreubotella) It's not good to throw an exception related to blob // TODO(andreubotella) It's not good to throw an exception related to blob
// URLs when none of the script URLs use the blob scheme. // URLs when none of the script URLs use the blob scheme.

View file

@ -37,7 +37,7 @@ use deno_core::SourceMapGetter;
use deno_fs::StdFs; use deno_fs::StdFs;
use deno_io::Stdio; use deno_io::Stdio;
use deno_kv::sqlite::SqliteDbHandler; use deno_kv::sqlite::SqliteDbHandler;
use deno_tls::rustls::RootCertStore; use deno_tls::RootCertStoreProvider;
use deno_web::create_entangled_message_port; use deno_web::create_entangled_message_port;
use deno_web::BlobStore; use deno_web::BlobStore;
use deno_web::MessagePort; use deno_web::MessagePort;
@ -329,7 +329,7 @@ pub struct WebWorkerOptions {
pub extensions: Vec<Extension>, pub extensions: Vec<Extension>,
pub startup_snapshot: Option<Snapshot>, pub startup_snapshot: Option<Snapshot>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>, pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub root_cert_store: Option<RootCertStore>, pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
pub seed: Option<u64>, pub seed: Option<u64>,
pub module_loader: Rc<dyn ModuleLoader>, pub module_loader: Rc<dyn ModuleLoader>,
pub node_fs: Option<Arc<dyn deno_node::NodeFs>>, pub node_fs: Option<Arc<dyn deno_node::NodeFs>>,
@ -407,7 +407,7 @@ impl WebWorker {
deno_fetch::deno_fetch::init_ops::<PermissionsContainer>( deno_fetch::deno_fetch::init_ops::<PermissionsContainer>(
deno_fetch::Options { deno_fetch::Options {
user_agent: options.bootstrap.user_agent.clone(), user_agent: options.bootstrap.user_agent.clone(),
root_cert_store: options.root_cert_store.clone(), root_cert_store_provider: options.root_cert_store_provider.clone(),
unsafely_ignore_certificate_errors: options unsafely_ignore_certificate_errors: options
.unsafely_ignore_certificate_errors .unsafely_ignore_certificate_errors
.clone(), .clone(),
@ -418,7 +418,7 @@ impl WebWorker {
deno_cache::deno_cache::init_ops::<SqliteBackedCache>(create_cache), deno_cache::deno_cache::init_ops::<SqliteBackedCache>(create_cache),
deno_websocket::deno_websocket::init_ops::<PermissionsContainer>( deno_websocket::deno_websocket::init_ops::<PermissionsContainer>(
options.bootstrap.user_agent.clone(), options.bootstrap.user_agent.clone(),
options.root_cert_store.clone(), options.root_cert_store_provider.clone(),
options.unsafely_ignore_certificate_errors.clone(), options.unsafely_ignore_certificate_errors.clone(),
), ),
deno_webstorage::deno_webstorage::init_ops(None).disable(), deno_webstorage::deno_webstorage::init_ops(None).disable(),
@ -429,7 +429,7 @@ impl WebWorker {
), ),
deno_ffi::deno_ffi::init_ops::<PermissionsContainer>(unstable), deno_ffi::deno_ffi::init_ops::<PermissionsContainer>(unstable),
deno_net::deno_net::init_ops::<PermissionsContainer>( deno_net::deno_net::init_ops::<PermissionsContainer>(
options.root_cert_store.clone(), options.root_cert_store_provider.clone(),
unstable, unstable,
options.unsafely_ignore_certificate_errors.clone(), options.unsafely_ignore_certificate_errors.clone(),
), ),

View file

@ -33,7 +33,7 @@ use deno_core::SourceMapGetter;
use deno_fs::StdFs; use deno_fs::StdFs;
use deno_io::Stdio; use deno_io::Stdio;
use deno_kv::sqlite::SqliteDbHandler; use deno_kv::sqlite::SqliteDbHandler;
use deno_tls::rustls::RootCertStore; use deno_tls::RootCertStoreProvider;
use deno_web::BlobStore; use deno_web::BlobStore;
use log::debug; use log::debug;
@ -84,7 +84,7 @@ pub struct WorkerOptions {
/// V8 snapshot that should be loaded on startup. /// V8 snapshot that should be loaded on startup.
pub startup_snapshot: Option<Snapshot>, pub startup_snapshot: Option<Snapshot>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>, pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
pub root_cert_store: Option<RootCertStore>, pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
pub seed: Option<u64>, pub seed: Option<u64>,
/// Implementation of `ModuleLoader` which will be /// Implementation of `ModuleLoader` which will be
@ -163,7 +163,7 @@ impl Default for WorkerOptions {
cache_storage_dir: Default::default(), cache_storage_dir: Default::default(),
broadcast_channel: Default::default(), broadcast_channel: Default::default(),
source_map_getter: Default::default(), source_map_getter: Default::default(),
root_cert_store: Default::default(), root_cert_store_provider: Default::default(),
node_fs: Default::default(), node_fs: Default::default(),
npm_resolver: Default::default(), npm_resolver: Default::default(),
blob_store: Default::default(), blob_store: Default::default(),
@ -228,7 +228,7 @@ impl MainWorker {
deno_fetch::deno_fetch::init_ops::<PermissionsContainer>( deno_fetch::deno_fetch::init_ops::<PermissionsContainer>(
deno_fetch::Options { deno_fetch::Options {
user_agent: options.bootstrap.user_agent.clone(), user_agent: options.bootstrap.user_agent.clone(),
root_cert_store: options.root_cert_store.clone(), root_cert_store_provider: options.root_cert_store_provider.clone(),
unsafely_ignore_certificate_errors: options unsafely_ignore_certificate_errors: options
.unsafely_ignore_certificate_errors .unsafely_ignore_certificate_errors
.clone(), .clone(),
@ -239,7 +239,7 @@ impl MainWorker {
deno_cache::deno_cache::init_ops::<SqliteBackedCache>(create_cache), deno_cache::deno_cache::init_ops::<SqliteBackedCache>(create_cache),
deno_websocket::deno_websocket::init_ops::<PermissionsContainer>( deno_websocket::deno_websocket::init_ops::<PermissionsContainer>(
options.bootstrap.user_agent.clone(), options.bootstrap.user_agent.clone(),
options.root_cert_store.clone(), options.root_cert_store_provider.clone(),
options.unsafely_ignore_certificate_errors.clone(), options.unsafely_ignore_certificate_errors.clone(),
), ),
deno_webstorage::deno_webstorage::init_ops( deno_webstorage::deno_webstorage::init_ops(
@ -252,7 +252,7 @@ impl MainWorker {
), ),
deno_ffi::deno_ffi::init_ops::<PermissionsContainer>(unstable), deno_ffi::deno_ffi::init_ops::<PermissionsContainer>(unstable),
deno_net::deno_net::init_ops::<PermissionsContainer>( deno_net::deno_net::init_ops::<PermissionsContainer>(
options.root_cert_store.clone(), options.root_cert_store_provider.clone(),
unstable, unstable,
options.unsafely_ignore_certificate_errors.clone(), options.unsafely_ignore_certificate_errors.clone(),
), ),