mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-23 04:55:21 +00:00
fix(db): always bypass system proxy for all HTTP-based database drivers
Replace per-driver proxy bypass logic with a shared http_client_builder() that always calls .no_proxy(). Previously ES and rqlite only bypassed for localhost, causing remote connections to be routed through system proxy (like Clash Verge) and failing — the same issue #922 fixed for ClickHouse.
This commit is contained in:
@@ -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<String>, password: Option<String>, 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<Self, String> {
|
||||
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 =
|
||||
|
||||
@@ -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<String> {
|
||||
}
|
||||
}
|
||||
|
||||
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::<std::net::IpAddr>().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<String, serde_json::Value>) -> (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"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Self, String> {
|
||||
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::<std::net::IpAddr>().is_ok_and(|ip| ip.is_loopback())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user