summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2022-01-24 11:27:52 +1100
committerGitHub <noreply@github.com>2022-01-24 11:27:52 +1100
commit3ec248cff8fff1a41c2b2ad5301e7aa3db00c6a8 (patch)
tree549b791b6d9718a51d3f7c8258e076beafab64df /cli
parent3959d9f2d285bd45beb34074cf61090e2c4976dc (diff)
fix(lsp): respect DENO_CERT and other options related to TLS certs (#13467)
Fixes #13437
Diffstat (limited to 'cli')
-rw-r--r--cli/file_fetcher.rs80
-rw-r--r--cli/lsp/cache.rs6
-rw-r--r--cli/lsp/config.rs16
-rw-r--r--cli/lsp/language_server.rs62
-rw-r--r--cli/lsp/registries.rs54
-rw-r--r--cli/lsp/repl.rs3
-rw-r--r--cli/proc_state.rs73
-rw-r--r--cli/tests/integration/lsp_tests.rs118
-rw-r--r--cli/tests/testdata/lsp/did_open_params_tls_cert.json8
-rw-r--r--cli/tests/testdata/lsp/initialize_params.json3
-rw-r--r--cli/tests/testdata/lsp/initialize_params_tls_cert.json71
11 files changed, 393 insertions, 101 deletions
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs
index 1ca04ed9a..92c253e31 100644
--- a/cli/file_fetcher.rs
+++ b/cli/file_fetcher.rs
@@ -12,6 +12,7 @@ use crate::version::get_user_agent;
use data_url::DataUrl;
use deno_ast::MediaType;
+use deno_core::anyhow::anyhow;
use deno_core::error::custom_error;
use deno_core::error::generic_error;
use deno_core::error::uri_error;
@@ -22,7 +23,11 @@ use deno_core::parking_lot::Mutex;
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::RootCertStore;
+use deno_runtime::deno_tls::rustls_native_certs::load_native_certs;
+use deno_runtime::deno_tls::rustls_pemfile;
+use deno_runtime::deno_tls::webpki_roots;
use deno_runtime::deno_web::BlobStore;
use deno_runtime::permissions::Permissions;
use log::debug;
@@ -31,6 +36,7 @@ use std::collections::HashMap;
use std::env;
use std::fs;
use std::future::Future;
+use std::io::BufReader;
use std::io::Read;
use std::path::PathBuf;
use std::pin::Pin;
@@ -161,6 +167,80 @@ fn fetch_local(specifier: &ModuleSpecifier) -> Result<File, AnyError> {
})
}
+/// Create and populate a root cert store based on the passed options and
+/// environment.
+pub(crate) fn get_root_cert_store(
+ maybe_root_path: Option<PathBuf>,
+ maybe_ca_stores: Option<Vec<String>>,
+ maybe_ca_file: Option<String>,
+) -> Result<RootCertStore, AnyError> {
+ let mut root_cert_store = RootCertStore::empty();
+ let ca_stores: Vec<String> = maybe_ca_stores
+ .or_else(|| {
+ let env_ca_store = env::var("DENO_TLS_CA_STORE").ok()?;
+ Some(
+ env_ca_store
+ .split(',')
+ .map(|s| s.trim().to_string())
+ .filter(|s| !s.is_empty())
+ .collect(),
+ )
+ })
+ .unwrap_or_else(|| vec!["mozilla".to_string()]);
+
+ for store in ca_stores.iter() {
+ match store.as_str() {
+ "mozilla" => {
+ root_cert_store.add_server_trust_anchors(
+ webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
+ rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
+ ta.subject,
+ ta.spki,
+ ta.name_constraints,
+ )
+ }),
+ );
+ }
+ "system" => {
+ let roots = load_native_certs().expect("could not load platform certs");
+ for root in roots {
+ root_cert_store
+ .add(&rustls::Certificate(root.0))
+ .expect("Failed to add platform cert to root cert store");
+ }
+ }
+ _ => {
+ return Err(anyhow!("Unknown certificate store \"{}\" specified (allowed: \"system,mozilla\")", store));
+ }
+ }
+ }
+
+ let ca_file = maybe_ca_file.or_else(|| env::var("DENO_CERT").ok());
+ if let Some(ca_file) = ca_file {
+ let ca_file = if let Some(root) = &maybe_root_path {
+ root.join(&ca_file)
+ } else {
+ PathBuf::from(ca_file)
+ };
+ let certfile = fs::File::open(&ca_file)?;
+ let mut reader = BufReader::new(certfile);
+
+ match rustls_pemfile::certs(&mut reader) {
+ Ok(certs) => {
+ root_cert_store.add_parsable_certificates(&certs);
+ }
+ Err(e) => {
+ return Err(anyhow!(
+ "Unable to add pem file to certificate store: {}",
+ e
+ ));
+ }
+ }
+ }
+
+ Ok(root_cert_store)
+}
+
/// Returns the decoded body and content-type of a provided
/// data URL.
pub fn get_source_from_data_url(
diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs
index 562ad78b0..e21bcff2d 100644
--- a/cli/lsp/cache.rs
+++ b/cli/lsp/cache.rs
@@ -33,6 +33,9 @@ impl CacheServer {
maybe_cache_path: Option<PathBuf>,
maybe_import_map: Option<Arc<ImportMap>>,
maybe_config_file: Option<ConfigFile>,
+ maybe_ca_stores: Option<Vec<String>>,
+ maybe_ca_file: Option<String>,
+ unsafely_ignore_certificate_errors: Option<Vec<String>>,
) -> Self {
let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
let _join_handle = thread::spawn(move || {
@@ -40,6 +43,9 @@ impl CacheServer {
runtime.block_on(async {
let ps = ProcState::build(Flags {
cache_path: maybe_cache_path,
+ ca_stores: maybe_ca_stores,
+ ca_file: maybe_ca_file,
+ unsafely_ignore_certificate_errors,
..Default::default()
})
.await
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs
index 89924a22a..37558e1b7 100644
--- a/cli/lsp/config.rs
+++ b/cli/lsp/config.rs
@@ -154,6 +154,10 @@ pub struct WorkspaceSettings {
/// cache/DENO_DIR for the language server.
pub cache: Option<String>,
+ /// Override the default stores used to validate certificates. This overrides
+ /// the environment variable `DENO_TLS_CA_STORE` if present.
+ pub certificate_stores: Option<Vec<String>>,
+
/// An option that points to a path string of the config file to apply to
/// code within the workspace.
pub config: Option<String>,
@@ -179,6 +183,15 @@ pub struct WorkspaceSettings {
#[serde(default)]
pub suggest: CompletionSettings,
+ /// An option which sets the cert file to use when attempting to fetch remote
+ /// resources. This overrides `DENO_CERT` if present.
+ pub tls_certificate: Option<String>,
+
+ /// An option, if set, will unsafely ignore certificate errors when fetching
+ /// remote resources.
+ #[serde(default)]
+ pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
+
#[serde(default)]
pub unstable: bool,
}
@@ -485,6 +498,7 @@ mod tests {
WorkspaceSettings {
enable: false,
cache: None,
+ certificate_stores: None,
config: None,
import_map: None,
code_lens: CodeLensSettings {
@@ -505,6 +519,8 @@ mod tests {
hosts: HashMap::new(),
}
},
+ tls_certificate: None,
+ unsafely_ignore_certificate_errors: None,
unstable: false,
}
);
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index e33ffc93a..5ad0c17a6 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -48,7 +48,8 @@ use super::lsp_custom;
use super::parent_process_checker;
use super::performance::Performance;
use super::refactor;
-use super::registries;
+use super::registries::ModuleRegistry;
+use super::registries::ModuleRegistryOptions;
use super::text;
use super::tsc;
use super::tsc::AssetDocument;
@@ -96,7 +97,7 @@ pub(crate) struct Inner {
/// on disk or "open" within the client.
pub(crate) documents: Documents,
/// Handles module registries, which allow discovery of modules
- module_registries: registries::ModuleRegistry,
+ module_registries: ModuleRegistry,
/// The path to the module registries cache
module_registries_location: PathBuf,
/// An optional path to the DENO_DIR which has been specified in the client
@@ -139,8 +140,11 @@ impl Inner {
let dir = deno_dir::DenoDir::new(maybe_custom_root)
.expect("could not access DENO_DIR");
let module_registries_location = dir.root.join(REGISTRIES_PATH);
- let module_registries =
- registries::ModuleRegistry::new(&module_registries_location);
+ let module_registries = ModuleRegistry::new(
+ &module_registries_location,
+ ModuleRegistryOptions::default(),
+ )
+ .expect("could not create module registries");
let location = dir.root.join(CACHE_PATH);
let documents = Documents::new(&location);
let performance = Arc::new(Performance::default());
@@ -425,11 +429,23 @@ impl Inner {
let maybe_custom_root = maybe_cache_path
.clone()
.or_else(|| env::var("DENO_DIR").map(String::into).ok());
- let dir = deno_dir::DenoDir::new(maybe_custom_root)
- .expect("could not access DENO_DIR");
+ let dir = deno_dir::DenoDir::new(maybe_custom_root)?;
let module_registries_location = dir.root.join(REGISTRIES_PATH);
- self.module_registries =
- registries::ModuleRegistry::new(&module_registries_location);
+ let workspace_settings = self.config.get_workspace_settings();
+ let maybe_root_path = self
+ .root_uri
+ .as_ref()
+ .and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
+ self.module_registries = ModuleRegistry::new(
+ &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,
+ },
+ )?;
self.module_registries_location = module_registries_location;
self.documents.set_location(dir.root.join(CACHE_PATH));
self.maybe_cache_path = maybe_cache_path;
@@ -496,14 +512,23 @@ impl Inner {
async fn update_registries(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_registries", None::<()>);
- for (registry, enabled) in self
- .config
- .get_workspace_settings()
- .suggest
- .imports
- .hosts
- .iter()
- {
+ let workspace_settings = self.config.get_workspace_settings();
+ let maybe_root_path = self
+ .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() {
if *enabled {
lsp_log!("Enabling import suggestions for: {}", registry);
self.module_registries.enable(registry).await?;
@@ -2583,6 +2608,9 @@ impl Inner {
self.maybe_cache_path.clone(),
self.maybe_import_map.clone(),
self.maybe_config_file.clone(),
+ None,
+ None,
+ None,
)
.await,
);
@@ -2616,8 +2644,6 @@ impl Inner {
error!("Unable to remove registries cache: {}", err);
LspError::internal_error()
})?;
- self.module_registries =
- registries::ModuleRegistry::new(&self.module_registries_location);
self.update_registries().await.map_err(|err| {
error!("Unable to update registries: {}", err);
LspError::internal_error()
diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs
index 418c19fe3..0b8a4240b 100644
--- a/cli/lsp/registries.rs
+++ b/cli/lsp/registries.rs
@@ -12,6 +12,7 @@ use super::path_to_regex::StringOrVec;
use super::path_to_regex::Token;
use crate::deno_dir;
+use crate::file_fetcher::get_root_cert_store;
use crate::file_fetcher::CacheSetting;
use crate::file_fetcher::FileFetcher;
use crate::http_cache::HttpCache;
@@ -37,6 +38,7 @@ use once_cell::sync::Lazy;
use regex::Regex;
use std::collections::HashMap;
use std::path::Path;
+use std::path::PathBuf;
const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json";
const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
@@ -406,11 +408,19 @@ enum VariableItems {
List(VariableItemsList),
}
+#[derive(Debug, Default)]
+pub(crate) 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
/// registries and can provide completion information for URLs that match
/// one of the enabled registries.
#[derive(Debug, Clone)]
-pub struct ModuleRegistry {
+pub(crate) struct ModuleRegistry {
origins: HashMap<String, Vec<RegistryConfiguration>>,
file_fetcher: FileFetcher,
}
@@ -422,29 +432,35 @@ impl Default for ModuleRegistry {
// custom root.
let dir = deno_dir::DenoDir::new(None).unwrap();
let location = dir.root.join("registries");
- Self::new(&location)
+ Self::new(&location, ModuleRegistryOptions::default()).unwrap()
}
}
impl ModuleRegistry {
- pub fn new(location: &Path) -> Self {
+ pub fn new(
+ location: &Path,
+ options: ModuleRegistryOptions,
+ ) -> Result<Self, AnyError> {
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(
http_cache,
CacheSetting::RespectHeaders,
true,
- None,
+ root_cert_store,
BlobStore::default(),
- None,
- )
- .context("Error creating file fetcher in module registry.")
- .unwrap();
+ options.unsafely_ignore_certificate_errors,
+ )?;
file_fetcher.set_download_log_level(super::logging::lsp_log_level());
- Self {
+ Ok(Self {
origins: HashMap::new(),
file_fetcher,
- }
+ })
}
fn complete_literal(
@@ -1200,7 +1216,8 @@ mod tests {
let _g = test_util::http_server();
let temp_dir = TempDir::new().expect("could not create tmp");
let location = temp_dir.path().join("registries");
- let mut module_registry = ModuleRegistry::new(&location);
+ let mut module_registry =
+ ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap();
module_registry
.enable("http://localhost:4545/")
.await
@@ -1260,7 +1277,8 @@ mod tests {
let _g = test_util::http_server();
let temp_dir = TempDir::new().expect("could not create tmp");
let location = temp_dir.path().join("registries");
- let mut module_registry = ModuleRegistry::new(&location);
+ let mut module_registry =
+ ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap();
module_registry
.enable("http://localhost:4545/")
.await
@@ -1482,7 +1500,8 @@ mod tests {
let _g = test_util::http_server();
let temp_dir = TempDir::new().expect("could not create tmp");
let location = temp_dir.path().join("registries");
- let mut module_registry = ModuleRegistry::new(&location);
+ let mut module_registry =
+ ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap();
module_registry
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json")
.await
@@ -1551,7 +1570,8 @@ mod tests {
let _g = test_util::http_server();
let temp_dir = TempDir::new().expect("could not create tmp");
let location = temp_dir.path().join("registries");
- let mut module_registry = ModuleRegistry::new(&location);
+ let mut module_registry =
+ ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap();
module_registry
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json")
.await
@@ -1601,7 +1621,8 @@ mod tests {
let _g = test_util::http_server();
let temp_dir = TempDir::new().expect("could not create tmp");
let location = temp_dir.path().join("registries");
- let module_registry = ModuleRegistry::new(&location);
+ let module_registry =
+ ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap();
let result = module_registry.check_origin("http://localhost:4545").await;
assert!(result.is_ok());
}
@@ -1611,7 +1632,8 @@ mod tests {
let _g = test_util::http_server();
let temp_dir = TempDir::new().expect("could not create tmp");
let location = temp_dir.path().join("registries");
- let module_registry = ModuleRegistry::new(&location);
+ let module_registry =
+ ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap();
let result = module_registry.check_origin("https://deno.com").await;
assert!(result.is_err());
let err = result.unwrap_err().to_string();
diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs
index b4bc1daf7..0a2603298 100644
--- a/cli/lsp/repl.rs
+++ b/cli/lsp/repl.rs
@@ -277,11 +277,14 @@ pub fn get_repl_workspace_settings() -> WorkspaceSettings {
WorkspaceSettings {
enable: true,
config: None,
+ certificate_stores: None,
cache: None,
import_map: None,
code_lens: Default::default(),
internal_debug: false,
lint: false,
+ tls_certificate: None,
+ unsafely_ignore_certificate_errors: None,
unstable: false,
suggest: CompletionSettings {
complete_function_calls: false,
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index 13a17b8fb..4264e32e3 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -8,6 +8,7 @@ use crate::config_file::ConfigFile;
use crate::config_file::MaybeImportsResult;
use crate::deno_dir;
use crate::emit;
+use crate::file_fetcher::get_root_cert_store;
use crate::file_fetcher::CacheSetting;
use crate::file_fetcher::FileFetcher;
use crate::flags;
@@ -42,11 +43,7 @@ use deno_graph::source::LoadFuture;
use deno_graph::source::Loader;
use deno_graph::MediaType;
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
-use deno_runtime::deno_tls::rustls;
use deno_runtime::deno_tls::rustls::RootCertStore;
-use deno_runtime::deno_tls::rustls_native_certs::load_native_certs;
-use deno_runtime::deno_tls::rustls_pemfile;
-use deno_runtime::deno_tls::webpki_roots;
use deno_runtime::deno_web::BlobStore;
use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::permissions::Permissions;
@@ -54,8 +51,6 @@ use import_map::ImportMap;
use log::warn;
use std::collections::HashSet;
use std::env;
-use std::fs::File;
-use std::io::BufReader;
use std::ops::Deref;
use std::sync::Arc;
@@ -101,67 +96,11 @@ impl ProcState {
let deps_cache_location = dir.root.join("deps");
let http_cache = http_cache::HttpCache::new(&deps_cache_location);
- let mut root_cert_store = RootCertStore::empty();
- let ca_stores: Vec<String> = flags
- .ca_stores
- .clone()
- .or_else(|| {
- let env_ca_store = env::var("DENO_TLS_CA_STORE").ok()?;
- Some(
- env_ca_store
- .split(',')
- .map(|s| s.trim().to_string())
- .filter(|s| !s.is_empty())
- .collect(),
- )
- })
- .unwrap_or_else(|| vec!["mozilla".to_string()]);
-
- for store in ca_stores.iter() {
- match store.as_str() {
- "mozilla" => {
- root_cert_store.add_server_trust_anchors(
- webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| {
- rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
- ta.subject,
- ta.spki,
- ta.name_constraints,
- )
- }),
- );
- }
- "system" => {
- let roots =
- load_native_certs().expect("could not load platform certs");
- for root in roots {
- root_cert_store
- .add(&rustls::Certificate(root.0))
- .expect("Failed to add platform cert to root cert store");
- }
- }
- _ => {
- return Err(anyhow!("Unknown certificate store \"{}\" specified (allowed: \"system,mozilla\")", store));
- }
- }
- }
-
- let ca_file = flags.ca_file.clone().or_else(|| env::var("DENO_CERT").ok());
- if let Some(ca_file) = ca_file {
- let certfile = File::open(&ca_file)?;
- let mut reader = BufReader::new(certfile);
-
- match rustls_pemfile::certs(&mut reader) {
- Ok(certs) => {
- root_cert_store.add_parsable_certificates(&certs);
- }
- Err(e) => {
- return Err(anyhow!(
- "Unable to add pem file to certificate store: {}",
- e
- ));
- }
- }
- }
+ let root_cert_store = get_root_cert_store(
+ None,
+ flags.ca_stores.clone(),
+ flags.ca_file.clone(),
+ )?;
if let Some(insecure_allowlist) =
flags.unsafely_ignore_certificate_errors.as_ref()
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index c992ca4cb..e831e8627 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -3216,6 +3216,124 @@ fn lsp_cache_location() {
shutdown(&mut client);
}
+/// Sets the TLS root certificate on startup, which allows the LSP to connect to
+/// the custom signed test server and be able to retrieve the registry config
+/// and cache files.
+#[test]
+fn lsp_tls_cert() {
+ let _g = http_server();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params_tls_cert.json"))
+ .unwrap();
+
+ params.root_uri = Some(Url::from_file_path(testdata_path()).unwrap());
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+ did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file_01.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "export const a = \"a\";\n",
+ }
+ }),
+ );
+ let diagnostics =
+ did_open(&mut client, load_fixture("did_open_params_tls_cert.json"));
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 14);
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "deno/cache",
+ json!({
+ "referrer": {
+ "uri": "file:///a/file.ts",
+ },
+ "uris": [],
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert!(maybe_res.is_some());
+ let (maybe_res, maybe_err) = client
+ .write_request(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 0,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: https&#8203;://localhost:5545/xTypeScriptTypes.js\n"
+ },
+ "range": {
+ "start": {
+ "line": 0,
+ "character": 19
+ },
+ "end":{
+ "line": 0,
+ "character": 63
+ }
+ }
+ }))
+ );
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ },
+ "position": {
+ "line": 7,
+ "character": 28
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": {
+ "kind": "markdown",
+ "value": "**Resolved Dependency**\n\n**Code**: http&#8203;://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts"
+ },
+ "range": {
+ "start": {
+ "line": 7,
+ "character": 19
+ },
+ "end": {
+ "line": 7,
+ "character": 53
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
#[test]
fn lsp_diagnostics_warn() {
let _g = http_server();
diff --git a/cli/tests/testdata/lsp/did_open_params_tls_cert.json b/cli/tests/testdata/lsp/did_open_params_tls_cert.json
new file mode 100644
index 000000000..2942bfcbe
--- /dev/null
+++ b/cli/tests/testdata/lsp/did_open_params_tls_cert.json
@@ -0,0 +1,8 @@
+{
+ "textDocument": {
+ "uri": "file:///a/file.ts",
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import * as a from \"https://localhost:5545/xTypeScriptTypes.js\";\n// @deno-types=\"https://localhost:5545/type_definitions/foo.d.ts\"\nimport * as b from \"https://localhost:5545/type_definitions/foo.js\";\nimport * as c from \"https://localhost:5545/subdir/type_reference.js\";\nimport * as d from \"https://localhost:5545/subdir/mod1.ts\";\nimport * as e from \"data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=\";\nimport * as f from \"./file_01.ts\";\nimport * as g from \"http://localhost:4545/x/a/mod.ts\";\n\nconsole.log(a, b, c, d, e, f, g);\n"
+ }
+}
diff --git a/cli/tests/testdata/lsp/initialize_params.json b/cli/tests/testdata/lsp/initialize_params.json
index 3b99be4f0..9fd197fe4 100644
--- a/cli/tests/testdata/lsp/initialize_params.json
+++ b/cli/tests/testdata/lsp/initialize_params.json
@@ -8,6 +8,7 @@
"initializationOptions": {
"enable": true,
"cache": null,
+ "certificateStores": null,
"codeLens": {
"implementations": true,
"references": true,
@@ -25,6 +26,8 @@
"hosts": {}
}
},
+ "tlsCertificate": null,
+ "unsafelyIgnoreCertificateErrors": null,
"unstable": false
},
"capabilities": {
diff --git a/cli/tests/testdata/lsp/initialize_params_tls_cert.json b/cli/tests/testdata/lsp/initialize_params_tls_cert.json
new file mode 100644
index 000000000..e231bf25f
--- /dev/null
+++ b/cli/tests/testdata/lsp/initialize_params_tls_cert.json
@@ -0,0 +1,71 @@
+{
+ "processId": 0,
+ "clientInfo": {
+ "name": "test-harness",
+ "version": "1.0.0"
+ },
+ "rootUri": null,
+ "initializationOptions": {
+ "enable": true,
+ "cache": null,
+ "certificateStores": null,
+ "codeLens": {
+ "implementations": true,
+ "references": true,
+ "test": true
+ },
+ "config": "",
+ "importMap": null,
+ "lint": true,
+ "suggest": {
+ "autoImports": true,
+ "completeFunctionCalls": false,
+ "names": true,
+ "paths": true,
+ "imports": {
+ "hosts": {
+ "https://localhost:5545": true,
+ "http://localhost:4545": true
+ }
+ }
+ },
+ "tlsCertificate": "tls/RootCA.pem",
+ "unsafelyIgnoreCertificateErrors": null,
+ "unstable": false
+ },
+ "capabilities": {
+ "textDocument": {
+ "codeAction": {
+ "codeActionLiteralSupport": {
+ "codeActionKind": {
+ "valueSet": [
+ "quickfix",
+ "refactor"
+ ]
+ }
+ },
+ "isPreferredSupport": true,
+ "dataSupport": true,
+ "disabledSupport": true,
+ "resolveSupport": {
+ "properties": [
+ "edit"
+ ]
+ }
+ },
+ "foldingRange": {
+ "lineFoldingOnly": true
+ },
+ "synchronization": {
+ "dynamicRegistration": true,
+ "willSave": true,
+ "willSaveWaitUntil": true,
+ "didSave": true
+ }
+ },
+ "workspace": {
+ "configuration": true,
+ "workspaceFolders": true
+ }
+ }
+}