diff --git a/crates/dbx-core/src/db/clickhouse_driver.rs b/crates/dbx-core/src/db/clickhouse_driver.rs index dbd77bc1be..aa6f91da75 100644 --- a/crates/dbx-core/src/db/clickhouse_driver.rs +++ b/crates/dbx-core/src/db/clickhouse_driver.rs @@ -3,7 +3,7 @@ use serde::Deserialize; use std::fs; use std::time::{Duration, Instant}; -use super::with_connection_timeout; +use super::{http_client_builder, with_connection_timeout}; use crate::query::MAX_ROWS; use crate::sql::starts_with_executable_sql_keyword; use crate::types::{ColumnInfo, DatabaseInfo, QueryResult, TableInfo}; @@ -17,8 +17,7 @@ pub struct ChClient { impl ChClient { pub fn new(url: &str, username: Option, password: Option, timeout: Duration) -> Self { - let http = - HttpClient::builder().connect_timeout(timeout).no_proxy().build().unwrap_or_else(|_| HttpClient::new()); + let http = http_client_builder(timeout).build().unwrap_or_else(|_| HttpClient::new()); Self { http, base_url: url.trim_end_matches('/').to_string(), username, password } } @@ -29,7 +28,7 @@ impl ChClient { ca_cert_path: Option<&str>, timeout: Duration, ) -> Result { - let mut builder = HttpClient::builder().connect_timeout(timeout).no_proxy(); + let mut builder = http_client_builder(timeout); if let Some(path) = ca_cert_path.map(str::trim).filter(|path| !path.is_empty()) { let path = expand_cert_path(path); let cert_bytes = diff --git a/crates/dbx-core/src/db/elasticsearch_driver.rs b/crates/dbx-core/src/db/elasticsearch_driver.rs index 3aeb5e4f5d..4f2086ae6b 100644 --- a/crates/dbx-core/src/db/elasticsearch_driver.rs +++ b/crates/dbx-core/src/db/elasticsearch_driver.rs @@ -5,7 +5,7 @@ use std::collections::HashSet; use std::error::Error; use std::time::Duration; -use super::with_connection_timeout; +use super::{http_client_builder, with_connection_timeout}; use crate::db::mongo_driver::MongoDocumentResult; pub struct EsClient { @@ -28,11 +28,7 @@ impl EsClient { (Some(u), Some(p)) if !u.is_empty() => Some((u.to_string(), p.to_string())), _ => None, }; - let mut builder = - HttpClient::builder().connect_timeout(timeout).danger_accept_invalid_certs(accept_invalid_certs); - if elasticsearch_should_bypass_system_proxy(&base_url) { - builder = builder.no_proxy(); - } + let builder = http_client_builder(timeout).danger_accept_invalid_certs(accept_invalid_certs); let http = builder.build().unwrap_or_else(|_| HttpClient::new()); let fallback_base_urls = elasticsearch_base_url_fallbacks(&base_url); Self { http, base_url, fallback_base_urls, auth } @@ -166,17 +162,6 @@ fn elasticsearch_base_url_fallbacks(base_url: &str) -> Vec { } } -fn elasticsearch_should_bypass_system_proxy(base_url: &str) -> bool { - let Ok(parsed) = reqwest::Url::parse(base_url) else { - return false; - }; - let Some(host) = parsed.host_str() else { - return false; - }; - let host = host.trim_matches(['[', ']']); - host.eq_ignore_ascii_case("localhost") || host.parse::().is_ok_and(|ip| ip.is_loopback()) -} - fn redact_elasticsearch_url(url: &str) -> String { let Ok(mut parsed) = reqwest::Url::parse(url) else { return url.to_string(); @@ -1149,8 +1134,7 @@ fn parse_aggregations(aggs: &serde_json::Map) -> (Vec #[cfg(test)] mod tests { use super::{ - elasticsearch_accept_invalid_certs, elasticsearch_base_url_fallbacks, elasticsearch_should_bypass_system_proxy, - redact_elasticsearch_url, EsClient, + elasticsearch_accept_invalid_certs, elasticsearch_base_url_fallbacks, redact_elasticsearch_url, EsClient, }; use std::time::Duration; @@ -1200,12 +1184,4 @@ mod tests { "https://user:password@localhost:9200" ); } - - #[test] - fn elasticsearch_only_bypasses_system_proxy_for_loopback_hosts() { - assert!(elasticsearch_should_bypass_system_proxy("https://localhost:9200")); - assert!(elasticsearch_should_bypass_system_proxy("https://127.0.0.1:9200")); - assert!(elasticsearch_should_bypass_system_proxy("https://[::1]:9200")); - assert!(!elasticsearch_should_bypass_system_proxy("https://search.example.com:9200")); - } } diff --git a/crates/dbx-core/src/db/mod.rs b/crates/dbx-core/src/db/mod.rs index f6e32f295f..91aa497724 100644 --- a/crates/dbx-core/src/db/mod.rs +++ b/crates/dbx-core/src/db/mod.rs @@ -16,6 +16,7 @@ pub mod sqlserver; pub mod ssh_tunnel; pub mod transport_layer_tunnel; +use reqwest::ClientBuilder; use std::future::Future; use std::time::Duration; @@ -30,6 +31,10 @@ pub fn connection_timeout() -> Duration { Duration::from_secs(CONNECTION_TIMEOUT_SECS) } +pub fn http_client_builder(timeout: Duration) -> ClientBuilder { + reqwest::Client::builder().connect_timeout(timeout).no_proxy() +} + const JS_MAX_SAFE_INTEGER: i64 = 9_007_199_254_740_991; pub fn safe_i64_to_json(v: i64) -> serde_json::Value { diff --git a/crates/dbx-core/src/db/rqlite_driver.rs b/crates/dbx-core/src/db/rqlite_driver.rs index 316fbee3ba..53651317db 100644 --- a/crates/dbx-core/src/db/rqlite_driver.rs +++ b/crates/dbx-core/src/db/rqlite_driver.rs @@ -2,7 +2,7 @@ use reqwest::Client as HttpClient; use serde::Deserialize; use std::time::{Duration, Instant}; -use super::with_connection_timeout; +use super::{http_client_builder, with_connection_timeout}; use crate::sql::starts_with_executable_sql_keyword; use crate::types::{ ColumnInfo, DatabaseInfo, ForeignKeyInfo, IndexInfo, ObjectSource, ObjectSourceKind, QueryResult, TableInfo, @@ -26,13 +26,10 @@ impl RqliteClient { tls_enabled: bool, timeout: Duration, ) -> Result { - let mut builder = HttpClient::builder().connect_timeout(timeout); + let mut builder = http_client_builder(timeout); if rqlite_accept_invalid_certs(tls_enabled, url_params) { builder = builder.danger_accept_invalid_certs(true); } - if rqlite_should_bypass_system_proxy(url) { - builder = builder.no_proxy(); - } let http = builder.build().map_err(|e| format!("Failed to configure rqlite HTTP client: {e}"))?; let auth = if username.trim().is_empty() { None } else { Some((username.to_string(), password.to_string())) }; Ok(Self { @@ -411,17 +408,6 @@ fn rqlite_accept_invalid_certs(tls_enabled: bool, url_params: Option<&str>) -> b }) } -fn rqlite_should_bypass_system_proxy(base_url: &str) -> bool { - let Ok(parsed) = reqwest::Url::parse(base_url) else { - return false; - }; - let Some(host) = parsed.host_str() else { - return false; - }; - let host = host.trim_matches(['[', ']']); - host.eq_ignore_ascii_case("localhost") || host.parse::().is_ok_and(|ip| ip.is_loopback()) -} - #[cfg(test)] mod tests { use super::*;