use hbb_common::{ async_recursion::async_recursion, config::{Config, Socks5Server}, log::{self, info}, proxy::{Proxy, ProxyScheme}, tls::{ get_cached_tls_accept_invalid_cert, get_cached_tls_type, is_plain, upsert_tls_cache, TlsType, }, }; use reqwest::{blocking::Client as SyncClient, Client as AsyncClient}; macro_rules! configure_http_client { ($builder:expr, $tls_type:expr, $danger_accept_invalid_cert:expr, $Client: ty) => {{ // https://github.com/rustdesk/rustdesk/issues/11569 // https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.no_proxy let mut builder = $builder.no_proxy(); match $tls_type { TlsType::Plain => {} TlsType::NativeTls => { builder = builder.use_native_tls(); if $danger_accept_invalid_cert { builder = builder.danger_accept_invalid_certs(true); } } TlsType::Rustls => { #[cfg(any(target_os = "android", target_os = "ios"))] match hbb_common::verifier::client_config($danger_accept_invalid_cert) { Ok(client_config) => { builder = builder.use_preconfigured_tls(client_config); } Err(e) => { hbb_common::log::error!("Failed to get client config: {}", e); } } #[cfg(not(any(target_os = "android", target_os = "ios")))] { builder = builder.use_rustls_tls(); if $danger_accept_invalid_cert { builder = builder.danger_accept_invalid_certs(true); } } } } let client = if let Some(conf) = Config::get_socks() { let proxy_result = Proxy::from_conf(&conf, None); match proxy_result { Ok(proxy) => { let proxy_setup = match &proxy.intercept { ProxyScheme::Http { host, .. } => { reqwest::Proxy::all(format!("http://{}", host)) } ProxyScheme::Https { host, .. } => { reqwest::Proxy::all(format!("https://{}", host)) } ProxyScheme::Socks5 { addr, .. } => { reqwest::Proxy::all(&format!("socks5://{}", addr)) } }; match proxy_setup { Ok(mut p) => { if let Some(auth) = proxy.intercept.maybe_auth() { if !auth.username().is_empty() && !auth.password().is_empty() { p = p.basic_auth(auth.username(), auth.password()); } } builder = builder.proxy(p); builder.build().unwrap_or_else(|e| { info!("Failed to create a proxied client: {}", e); <$Client>::new() }) } Err(e) => { info!("Failed to set up proxy: {}", e); <$Client>::new() } } } Err(e) => { info!("Failed to configure proxy: {}", e); <$Client>::new() } } } else { builder.build().unwrap_or_else(|e| { info!("Failed to create a client: {}", e); <$Client>::new() }) }; client }}; } pub fn create_http_client(tls_type: TlsType, danger_accept_invalid_cert: bool) -> SyncClient { let builder = SyncClient::builder(); configure_http_client!(builder, tls_type, danger_accept_invalid_cert, SyncClient) } pub fn create_http_client_async( tls_type: TlsType, danger_accept_invalid_cert: bool, ) -> AsyncClient { let builder = AsyncClient::builder(); configure_http_client!(builder, tls_type, danger_accept_invalid_cert, AsyncClient) } pub fn get_url_for_tls<'a>(url: &'a str, proxy_conf: &'a Option) -> &'a str { if is_plain(url) { if let Some(conf) = proxy_conf { if conf.proxy.starts_with("https://") { return &conf.proxy; } } } url } pub fn create_http_client_with_url(url: &str) -> SyncClient { let proxy_conf = Config::get_socks(); let tls_url = get_url_for_tls(url, &proxy_conf); let tls_type = get_cached_tls_type(tls_url); let is_tls_type_cached = tls_type.is_some(); let tls_type = tls_type.unwrap_or(TlsType::Rustls); let tls_danger_accept_invalid_cert = get_cached_tls_accept_invalid_cert(tls_url); create_http_client_with_url_( url, tls_url, tls_type, is_tls_type_cached, tls_danger_accept_invalid_cert, tls_danger_accept_invalid_cert, ) } fn create_http_client_with_url_( url: &str, tls_url: &str, tls_type: TlsType, is_tls_type_cached: bool, danger_accept_invalid_cert: Option, original_danger_accept_invalid_cert: Option, ) -> SyncClient { let mut client = create_http_client(tls_type, danger_accept_invalid_cert.unwrap_or(false)); if is_tls_type_cached && original_danger_accept_invalid_cert.is_some() { return client; } if let Err(e) = client.head(url).send() { if e.is_request() { match (tls_type, is_tls_type_cached, danger_accept_invalid_cert) { (TlsType::Rustls, _, None) => { log::warn!( "Failed to connect to server {} with rustls-tls: {:?}, trying accept invalid cert", tls_url, e ); client = create_http_client_with_url_( url, tls_url, tls_type, is_tls_type_cached, Some(true), original_danger_accept_invalid_cert, ); } (TlsType::Rustls, false, Some(_)) => { log::warn!( "Failed to connect to server {} with rustls-tls: {:?}, trying native-tls", tls_url, e ); client = create_http_client_with_url_( url, tls_url, TlsType::NativeTls, is_tls_type_cached, original_danger_accept_invalid_cert, original_danger_accept_invalid_cert, ); } (TlsType::NativeTls, _, None) => { log::warn!( "Failed to connect to server {} with native-tls: {:?}, trying accept invalid cert", tls_url, e ); client = create_http_client_with_url_( url, tls_url, tls_type, is_tls_type_cached, Some(true), original_danger_accept_invalid_cert, ); } _ => { log::error!( "Failed to connect to server {} with {:?}, err: {:?}.", tls_url, tls_type, e ); } } } else { log::warn!( "Failed to connect to server {} with {:?}, err: {}.", tls_url, tls_type, e ); } } else { log::info!( "Successfully connected to server {} with {:?}", tls_url, tls_type ); upsert_tls_cache( tls_url, tls_type, danger_accept_invalid_cert.unwrap_or(false), ); } client } pub async fn create_http_client_async_with_url(url: &str) -> AsyncClient { let proxy_conf = Config::get_socks(); let tls_url = get_url_for_tls(url, &proxy_conf); let tls_type = get_cached_tls_type(tls_url); let is_tls_type_cached = tls_type.is_some(); let tls_type = tls_type.unwrap_or(TlsType::Rustls); let danger_accept_invalid_cert = get_cached_tls_accept_invalid_cert(tls_url); create_http_client_async_with_url_( url, tls_url, tls_type, is_tls_type_cached, danger_accept_invalid_cert, danger_accept_invalid_cert, ) .await } #[async_recursion] async fn create_http_client_async_with_url_( url: &str, tls_url: &str, tls_type: TlsType, is_tls_type_cached: bool, danger_accept_invalid_cert: Option, original_danger_accept_invalid_cert: Option, ) -> AsyncClient { let mut client = create_http_client_async(tls_type, danger_accept_invalid_cert.unwrap_or(false)); if is_tls_type_cached && original_danger_accept_invalid_cert.is_some() { return client; } if let Err(e) = client.head(url).send().await { match (tls_type, is_tls_type_cached, danger_accept_invalid_cert) { (TlsType::Rustls, _, None) => { log::warn!( "Failed to connect to server {} with rustls-tls: {:?}, trying accept invalid cert", tls_url, e ); client = create_http_client_async_with_url_( url, tls_url, tls_type, is_tls_type_cached, Some(true), original_danger_accept_invalid_cert, ) .await; } (TlsType::Rustls, false, Some(_)) => { log::warn!( "Failed to connect to server {} with rustls-tls: {:?}, trying native-tls", tls_url, e ); client = create_http_client_async_with_url_( url, tls_url, TlsType::NativeTls, is_tls_type_cached, original_danger_accept_invalid_cert, original_danger_accept_invalid_cert, ) .await; } (TlsType::NativeTls, _, None) => { log::warn!( "Failed to connect to server {} with native-tls: {:?}, trying accept invalid cert", tls_url, e ); client = create_http_client_async_with_url_( url, tls_url, tls_type, is_tls_type_cached, Some(true), original_danger_accept_invalid_cert, ) .await; } _ => { log::error!( "Failed to connect to server {} with {:?}, err: {:?}.", tls_url, tls_type, e ); } } } else { log::info!( "Successfully connected to server {} with {:?}", tls_url, tls_type ); upsert_tls_cache( tls_url, tls_type, danger_accept_invalid_cert.unwrap_or(false), ); } client }