summaryrefslogtreecommitdiff
path: root/extensions/fetch
diff options
context:
space:
mode:
authorJustin Chase <justin.m.chase@gmail.com>2021-08-07 07:49:38 -0500
committerGitHub <noreply@github.com>2021-08-07 14:49:38 +0200
commit02c74fb70970fcadb7d1e6dab857eeb2cea20e09 (patch)
tree03a1490e063bca34be660eee73bccc8342b0bff2 /extensions/fetch
parentfddeb4cea2687b32a32f7829f336b7cf5092c714 (diff)
feat(tls): Optionally support loading native certs (#11491)
This commit adds "DENO_TLS_CA_STORE" env variable to support optionally loading certificates from the users local certificate store. This will allow them to successfully connect via tls with corporate and self signed certs provided they have them installed in their keystore. It also allows them to deal with revoked certs by simply updating their keystore without having to upgrade Deno. Currently supported values are "mozilla", "system" or empty value.
Diffstat (limited to 'extensions/fetch')
-rw-r--r--extensions/fetch/Cargo.toml2
-rw-r--r--extensions/fetch/lib.rs78
2 files changed, 19 insertions, 61 deletions
diff --git a/extensions/fetch/Cargo.toml b/extensions/fetch/Cargo.toml
index 9c9a64222..211071236 100644
--- a/extensions/fetch/Cargo.toml
+++ b/extensions/fetch/Cargo.toml
@@ -17,8 +17,10 @@ path = "lib.rs"
bytes = "1.0.1"
data-url = "0.1.0"
deno_core = { version = "0.95.0", path = "../../core" }
+deno_tls = { version = "0.1.0", path = "../tls" }
deno_web = { version = "0.44.0", path = "../web" }
http = "0.2.4"
+lazy_static = "1.4.0"
reqwest = { version = "0.11.4", default-features = false, features = ["rustls-tls", "stream", "gzip", "brotli"] }
serde = { version = "1.0.126", features = ["derive"] }
tokio = { version = "1.8.1", features = ["full"] }
diff --git a/extensions/fetch/lib.rs b/extensions/fetch/lib.rs
index ad599b87c..0ac853cbc 100644
--- a/extensions/fetch/lib.rs
+++ b/extensions/fetch/lib.rs
@@ -1,7 +1,7 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+use data_url::DataUrl;
use deno_core::error::bad_resource_id;
-use deno_core::error::generic_error;
use deno_core::error::null_opbuf;
use deno_core::error::type_error;
use deno_core::error::AnyError;
@@ -24,16 +24,14 @@ use deno_core::RcRef;
use deno_core::Resource;
use deno_core::ResourceId;
use deno_core::ZeroCopyBuf;
-
-use data_url::DataUrl;
+use deno_tls::create_http_client;
+use deno_tls::rustls::RootCertStore;
+use deno_tls::Proxy;
use deno_web::BlobStore;
use http::header::CONTENT_LENGTH;
-use reqwest::header::HeaderMap;
use reqwest::header::HeaderName;
use reqwest::header::HeaderValue;
use reqwest::header::HOST;
-use reqwest::header::USER_AGENT;
-use reqwest::redirect::Policy;
use reqwest::Body;
use reqwest::Client;
use reqwest::Method;
@@ -59,7 +57,7 @@ pub use reqwest; // Re-export reqwest
pub fn init<P: FetchPermissions + 'static>(
user_agent: String,
- ca_data: Option<Vec<u8>>,
+ root_cert_store: Option<RootCertStore>,
proxy: Option<Proxy>,
request_builder_hook: Option<fn(RequestBuilder) -> RequestBuilder>,
) -> Extension {
@@ -84,12 +82,17 @@ pub fn init<P: FetchPermissions + 'static>(
])
.state(move |state| {
state.put::<reqwest::Client>({
- create_http_client(user_agent.clone(), ca_data.clone(), proxy.clone())
- .unwrap()
+ create_http_client(
+ user_agent.clone(),
+ root_cert_store.clone(),
+ None,
+ proxy.clone(),
+ )
+ .unwrap()
});
state.put::<HttpClientDefaults>(HttpClientDefaults {
- ca_data: ca_data.clone(),
user_agent: user_agent.clone(),
+ root_cert_store: root_cert_store.clone(),
proxy: proxy.clone(),
request_builder_hook,
});
@@ -100,7 +103,7 @@ pub fn init<P: FetchPermissions + 'static>(
pub struct HttpClientDefaults {
pub user_agent: String,
- pub ca_data: Option<Vec<u8>>,
+ pub root_cert_store: Option<RootCertStore>,
pub proxy: Option<Proxy>,
pub request_builder_hook: Option<fn(RequestBuilder) -> RequestBuilder>,
}
@@ -501,26 +504,12 @@ impl HttpClientResource {
#[serde(rename_all = "camelCase")]
#[serde(default)]
pub struct CreateHttpClientOptions {
+ ca_stores: Option<Vec<String>>,
ca_file: Option<String>,
ca_data: Option<ByteString>,
proxy: Option<Proxy>,
}
-#[derive(Deserialize, Default, Debug, Clone)]
-#[serde(rename_all = "camelCase")]
-#[serde(default)]
-pub struct Proxy {
- pub url: String,
- pub basic_auth: Option<BasicAuth>,
-}
-
-#[derive(Deserialize, Default, Debug, Clone)]
-#[serde(default)]
-pub struct BasicAuth {
- pub username: String,
- pub password: String,
-}
-
pub fn op_create_http_client<FP>(
state: &mut OpState,
args: CreateHttpClientOptions,
@@ -541,12 +530,12 @@ where
}
let defaults = state.borrow::<HttpClientDefaults>();
-
let cert_data =
get_cert_data(args.ca_file.as_deref(), args.ca_data.as_deref())?;
let client = create_http_client(
defaults.user_agent.clone(),
- cert_data.or_else(|| defaults.ca_data.clone()),
+ defaults.root_cert_store.clone(),
+ cert_data,
args.proxy,
)
.unwrap();
@@ -569,36 +558,3 @@ fn get_cert_data(
Ok(None)
}
}
-
-/// Create new instance of async reqwest::Client. This client supports
-/// proxies and doesn't follow redirects.
-pub fn create_http_client(
- user_agent: String,
- ca_data: Option<Vec<u8>>,
- proxy: Option<Proxy>,
-) -> Result<Client, AnyError> {
- let mut headers = HeaderMap::new();
- headers.insert(USER_AGENT, user_agent.parse().unwrap());
- let mut builder = Client::builder()
- .redirect(Policy::none())
- .default_headers(headers)
- .use_rustls_tls();
-
- if let Some(ca_data) = ca_data {
- let cert = reqwest::Certificate::from_pem(&ca_data)?;
- builder = builder.add_root_certificate(cert);
- }
-
- if let Some(proxy) = proxy {
- let mut reqwest_proxy = reqwest::Proxy::all(&proxy.url)?;
- if let Some(basic_auth) = &proxy.basic_auth {
- reqwest_proxy =
- reqwest_proxy.basic_auth(&basic_auth.username, &basic_auth.password);
- }
- builder = builder.proxy(reqwest_proxy);
- }
-
- builder
- .build()
- .map_err(|e| generic_error(format!("Unable to build http client: {}", e)))
-}