diff options
Diffstat (limited to 'ext')
-rw-r--r-- | ext/fetch/lib.rs | 73 | ||||
-rw-r--r-- | ext/net/lib.rs | 17 | ||||
-rw-r--r-- | ext/net/ops_tls.rs | 9 | ||||
-rw-r--r-- | ext/tls/lib.rs | 8 | ||||
-rw-r--r-- | ext/websocket/lib.rs | 22 |
5 files changed, 89 insertions, 40 deletions
diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 17f30d8ed..51688a6fc 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -3,7 +3,16 @@ mod byte_stream; 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::AnyError; use deno_core::futures::stream::Peekable; @@ -29,6 +38,9 @@ use deno_core::ResourceId; use deno_core::ZeroCopyBuf; use deno_tls::rustls::RootCertStore; use deno_tls::Proxy; +use deno_tls::RootCertStoreProvider; + +use data_url::DataUrl; use http::header::CONTENT_LENGTH; use http::Uri; use reqwest::header::HeaderMap; @@ -46,14 +58,6 @@ use reqwest::RequestBuilder; use reqwest::Response; use serde::Deserialize; 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; // Re-export reqwest and data_url @@ -67,7 +71,7 @@ use crate::byte_stream::MpscByteStream; #[derive(Clone)] pub struct Options { pub user_agent: String, - pub root_cert_store: Option<RootCertStore>, + pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>, pub proxy: Option<Proxy>, pub request_builder_hook: Option<fn(RequestBuilder) -> Result<RequestBuilder, AnyError>>, @@ -76,11 +80,20 @@ pub struct Options { 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 { fn default() -> Self { Self { user_agent: "".to_string(), - root_cert_store: None, + root_cert_store_provider: None, proxy: None, request_builder_hook: None, unsafely_ignore_certificate_errors: None, @@ -111,18 +124,7 @@ deno_core::extension!(deno_fetch, options: Options, }, state = |state, options| { - state.put::<Options>(options.options.clone()); - 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() - }); + state.put::<Options>(options.options); }, ); @@ -189,6 +191,26 @@ pub struct FetchReturn { 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] pub fn op_fetch<FP>( state: &mut OpState, @@ -207,8 +229,7 @@ where let r = state.resource_table.get::<HttpClientResource>(rid)?; r.client.clone() } else { - let client = state.borrow::<reqwest::Client>(); - client.clone() + get_or_create_client_from_state(state)? }; let method = Method::from_bytes(&method)?; @@ -632,7 +653,7 @@ where let client = create_http_client( &options.user_agent, - options.root_cert_store.clone(), + options.root_cert_store()?, ca_certs, args.proxy, options.unsafely_ignore_certificate_errors.clone(), diff --git a/ext/net/lib.rs b/ext/net/lib.rs index ff67186b0..912b0723e 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -11,10 +11,12 @@ pub mod resolve_addr; use deno_core::error::AnyError; use deno_core::OpState; use deno_tls::rustls::RootCertStore; +use deno_tls::RootCertStoreProvider; use std::cell::RefCell; use std::path::Path; use std::path::PathBuf; use std::rc::Rc; +use std::sync::Arc; pub trait NetPermissions { fn check_net<T: AsRef<str>>( @@ -67,7 +69,16 @@ pub fn get_declaration() -> PathBuf { #[derive(Clone)] 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`; @@ -113,13 +124,13 @@ deno_core::extension!(deno_net, ], esm = [ "01_net.js", "02_tls.js" ], options = { - root_cert_store: Option<RootCertStore>, + root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>, unstable: bool, unsafely_ignore_certificate_errors: Option<Vec<String>>, }, state = |state, options| { 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(UnsafelyIgnoreCertificateErrors( diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index 8a7757066..b9b37b328 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -813,14 +813,10 @@ where .try_borrow::<UnsafelyIgnoreCertificateErrors>() .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 .borrow() .borrow::<DefaultTlsOptions>() - .root_cert_store - .clone(); + .root_cert_store()?; let resource_rc = state .borrow_mut() @@ -912,8 +908,7 @@ where let root_cert_store = state .borrow() .borrow::<DefaultTlsOptions>() - .root_cert_store - .clone(); + .root_cert_store()?; let hostname_dns = ServerName::try_from(&*addr.hostname) .map_err(|_| invalid_hostname(&addr.hostname))?; let connect_addr = resolve_addr(&addr.hostname, addr.port) diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs index 123d35acf..3034e2ae9 100644 --- a/ext/tls/lib.rs +++ b/ext/tls/lib.rs @@ -34,6 +34,14 @@ use std::io::Cursor; use std::sync::Arc; 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. deno_core::extension!(deno_tls); diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index 2ce141fc9..e03a13789 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -19,6 +19,7 @@ use deno_core::ZeroCopyBuf; use deno_net::raw::take_network_stream_resource; use deno_net::raw::NetworkStream; use deno_tls::create_client_config; +use deno_tls::RootCertStoreProvider; use http::header::CONNECTION; use http::header::UPGRADE; use http::HeaderName; @@ -54,7 +55,17 @@ use fastwebsockets::WebSocket; mod stream; #[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)] pub struct WsUserAgent(pub String); @@ -181,7 +192,10 @@ where .borrow() .try_borrow::<UnsafelyIgnoreCertificateErrors>() .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 uri: Uri = url.parse()?; 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" ], options = { user_agent: String, - root_cert_store: Option<RootCertStore>, + root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>, unsafely_ignore_certificate_errors: Option<Vec<String>> }, state = |state, options| { @@ -533,7 +547,7 @@ deno_core::extension!(deno_websocket, state.put(UnsafelyIgnoreCertificateErrors( options.unsafely_ignore_certificate_errors, )); - state.put::<WsRootStore>(WsRootStore(options.root_cert_store)); + state.put::<WsRootStoreProvider>(WsRootStoreProvider(options.root_cert_store_provider)); }, ); |