From 00f68d347c75ea061bc6f12255a053bec6f08e5a Mon Sep 17 00:00:00 2001 From: Shivansh Mathur Date: Mon, 13 Oct 2025 12:48:46 +0530 Subject: [PATCH] config of https and http curl --- crates/router/src/core/payments/helpers.rs | 196 ++++++++++++++++-- .../src/core/unified_connector_service.rs | 30 ++- 2 files changed, 205 insertions(+), 21 deletions(-) diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 601d9bc5f9..69408f2ede 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -2106,34 +2106,198 @@ pub async fn is_ucs_enabled(state: &SessionState, config_key: &str) -> bool { .unwrap_or(false) } +#[derive(Debug, Clone, Deserialize)] +pub struct RolloutConfig { + pub rollout_percent: f64, + pub http_url: Option, + pub https_url: Option, +} + +impl Default for RolloutConfig { + fn default() -> Self { + Self { + rollout_percent: 0.0, + http_url: None, + https_url: None, + } + } +} + +#[derive(Debug, Clone)] +pub struct ProxyOverride { + pub http_url: Option, + pub https_url: Option, +} + +#[derive(Debug, Clone)] +pub struct RolloutExecutionResult { + pub should_execute: bool, + pub proxy_override: Option, +} + +/// Effective proxy configuration with rollout override support +#[derive(Debug, Clone)] +pub struct EffectiveProxyConfig<'a> { + base_proxy: &'a hyperswitch_interfaces::types::Proxy, + proxy_override: Option<&'a ProxyOverride>, +} + +impl<'a> EffectiveProxyConfig<'a> { + /// Create a new effective proxy config + pub fn new( + base_proxy: &'a hyperswitch_interfaces::types::Proxy, + proxy_override: Option<&'a ProxyOverride>, + ) -> Self { + Self { + base_proxy, + proxy_override, + } + } + + /// Get HTTP proxy URL (override takes precedence) + pub fn http_url(&self) -> Option<&String> { + self.proxy_override + .and_then(|override_| override_.http_url.as_ref()) + .or(self.base_proxy.http_url.as_ref()) + } + + /// Get HTTPS proxy URL (override takes precedence) + pub fn https_url(&self) -> Option<&String> { + self.proxy_override + .and_then(|override_| override_.https_url.as_ref()) + .or(self.base_proxy.https_url.as_ref()) + } + + /// Get any proxy URL, preferring HTTPS over HTTP + pub fn any_url(&self, prefer_https: bool) -> Option<&String> { + if prefer_https { + self.https_url().or(self.http_url()) + } else { + self.http_url().or(self.https_url()) + } + } + + /// Get proxy URL for reqwest client (cloned) + pub fn for_reqwest(&self, prefer_https: bool) -> Option { + self.any_url(prefer_https).cloned() + } + + /// Check if any proxy is configured + pub fn has_proxy(&self) -> bool { + self.http_url().is_some() || self.https_url().is_some() + } + + /// Get all proxy configuration details + pub fn get_config(&self) -> ProxyConfig { + ProxyConfig { + http_url: self.http_url().cloned(), + https_url: self.https_url().cloned(), + idle_pool_connection_timeout: self.base_proxy.idle_pool_connection_timeout, + bypass_proxy_hosts: self.base_proxy.bypass_proxy_hosts.clone(), + mitm_ca_certificate: self.base_proxy.mitm_ca_certificate.clone(), + mitm_enabled: self.base_proxy.mitm_enabled, + } + } +} + +/// Resolved proxy configuration +#[derive(Debug, Clone)] +pub struct ProxyConfig { + pub http_url: Option, + pub https_url: Option, + pub idle_pool_connection_timeout: Option, + pub bypass_proxy_hosts: Option, + pub mitm_ca_certificate: Option>, + pub mitm_enabled: Option, +} + pub async fn should_execute_based_on_rollout( state: &SessionState, config_key: &str, -) -> RouterResult { +) -> RouterResult { let db = state.store.as_ref(); match db.find_config_by_key(config_key).await { - Ok(rollout_config) => match rollout_config.config.parse::() { - Ok(rollout_percent) => { - if !(0.0..=1.0).contains(&rollout_percent) { - logger::warn!( - rollout_percent, - "Rollout percent out of bounds. Must be between 0.0 and 1.0" - ); - return Ok(false); + Ok(rollout_config) => { + // Try to parse as JSON first (new format), fallback to float (legacy format) + let config_result = match serde_json::from_str::(&rollout_config.config) { + Ok(config) => Ok(config), + Err(_) => { + // Fallback to legacy format (simple float) + rollout_config.config.parse::() + .map(|percent| RolloutConfig { + rollout_percent: percent, + http_url: None, + https_url: None, + }) + .map_err(|err| { + logger::error!(error = ?err, "Failed to parse rollout config as either JSON or float"); + err + }) } + }; - let sampled_value: f64 = rand::thread_rng().gen_range(0.0..1.0); - Ok(sampled_value < rollout_percent) - } - Err(err) => { - logger::error!(error = ?err, "Failed to parse rollout percent"); - Ok(false) + match config_result { + Ok(config) => { + if !(0.0..=1.0).contains(&config.rollout_percent) { + logger::warn!( + rollout_percent = config.rollout_percent, + "Rollout percent out of bounds. Must be between 0.0 and 1.0" + ); + let proxy_override = if config.http_url.is_some() || config.https_url.is_some() { + Some(ProxyOverride { + http_url: config.http_url, + https_url: config.https_url, + }) + } else { + None + }; + + return Ok(RolloutExecutionResult { + should_execute: false, + proxy_override, + }); + } + + let sampled_value: f64 = rand::thread_rng().gen_range(0.0..1.0); + let should_execute = sampled_value < config.rollout_percent; + + // Create proxy override if URLs are available + let proxy_override = if config.http_url.is_some() || config.https_url.is_some() { + if let Some(ref http_url) = config.http_url { + logger::info!(http_url = %http_url, "Using HTTP proxy URL from rollout config"); + } + if let Some(ref https_url) = config.https_url { + logger::info!(https_url = %https_url, "Using HTTPS proxy URL from rollout config"); + } + Some(ProxyOverride { + http_url: config.http_url, + https_url: config.https_url, + }) + } else { + None + }; + + Ok(RolloutExecutionResult { + should_execute, + proxy_override, + }) + } + Err(err) => { + logger::error!(error = ?err, "Failed to parse rollout config"); + Ok(RolloutExecutionResult { + should_execute: false, + proxy_override: None, + }) + } } }, Err(err) => { logger::error!(error = ?err, "Failed to fetch rollout config from DB"); - Ok(false) + Ok(RolloutExecutionResult { + should_execute: false, + proxy_override: None, + }) } } } diff --git a/crates/router/src/core/unified_connector_service.rs b/crates/router/src/core/unified_connector_service.rs index 04a3a2e4ef..e4dcea8a55 100644 --- a/crates/router/src/core/unified_connector_service.rs +++ b/crates/router/src/core/unified_connector_service.rs @@ -49,7 +49,8 @@ use crate::{ errors::{self, RouterResult}, payments::{ helpers::{ - is_ucs_enabled, should_execute_based_on_rollout, MerchantConnectorAccountType, + is_ucs_enabled, should_execute_based_on_rollout, MerchantConnectorAccountType, + RolloutExecutionResult, ProxyOverride, EffectiveProxyConfig, ProxyConfig, }, OperationSessionGetters, OperationSessionSetters, }, @@ -165,9 +166,28 @@ where ); let shadow_rollout_key = format!("{}_shadow", rollout_key); - let rollout_enabled = should_execute_based_on_rollout(state, &rollout_key).await?; - let shadow_rollout_enabled = + let rollout_result = should_execute_based_on_rollout(state, &rollout_key).await?; + let shadow_rollout_result = should_execute_based_on_rollout(state, &shadow_rollout_key).await?; + + let rollout_enabled = rollout_result.should_execute; + let shadow_rollout_enabled = shadow_rollout_result.should_execute; + + // Log proxy URLs if available + if let Some(ref proxy_override) = rollout_result.proxy_override { + router_env::logger::info!( + http_url = ?proxy_override.http_url, + https_url = ?proxy_override.https_url, + "Main rollout config has proxy URLs" + ); + } + if let Some(ref proxy_override) = shadow_rollout_result.proxy_override { + router_env::logger::info!( + http_url = ?proxy_override.http_url, + https_url = ?proxy_override.https_url, + "Shadow rollout config has proxy URLs" + ); + } router_env::logger::debug!( "Rollout status - rollout_enabled={}, shadow_rollout_enabled={}, rollout_key={}, merchant_id={}, connector={}", @@ -433,9 +453,9 @@ pub async fn should_call_unified_connector_service_for_webhooks( connector_name ); - let should_execute = should_execute_based_on_rollout(state, &config_key).await?; + let rollout_result = should_execute_based_on_rollout(state, &config_key).await?; - Ok(should_execute) + Ok(rollout_result.should_execute) } pub fn build_unified_connector_service_payment_method(