From 51c6d4c5ed41f613a3d822c0e4b64a2b7bfa31be Mon Sep 17 00:00:00 2001 From: Apoorv Raj Saxena Date: Sat, 4 Jul 2026 16:14:02 +0400 Subject: [PATCH 1/2] fix: enable upstream connect retry by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tier 2.7 of the network-robustness audit. The single-retry-on-transient- failure logic (already implemented in soth-mitm) was gated behind upstream_retry_on_failure, which defaulted to false — so the knob was effectively dead and a single transient connect failure during a hotspot handoff or gateway blip surfaced straight to the user as a 502. Flip the default to true in both the CLI-generated config (cli_config ForwardProxyConfig) and the soth-proxy ProxyConfig fallback. The retry fires only on transient errors (refused / unreachable / reset), never on timeouts, and is bounded by the existing connect deadline — so it absorbs handoff blips without masking a genuinely stuck upstream. Users can still set it false explicitly. soth-proxy + soth-cli config tests pass. Co-Authored-By: Claude Fable 5 --- crates/soth-cli/src/cli_config.rs | 8 +++++++- crates/soth-proxy/src/config.rs | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/crates/soth-cli/src/cli_config.rs b/crates/soth-cli/src/cli_config.rs index 81620722..e7721a33 100644 --- a/crates/soth-cli/src/cli_config.rs +++ b/crates/soth-cli/src/cli_config.rs @@ -98,7 +98,13 @@ impl Default for ForwardProxyConfig { // response. 120s gives streaming LLM responses the headroom // they need without hiding genuinely-stuck upstreams. upstream_timeout: DurationSetting::millis(120_000), - upstream_retry_on_failure: false, + // On by default: a single retry (after upstream_retry_delay, bounded + // by the connect deadline) for transient connect failures — refused + // / unreachable / reset — which are routine during hotspot handoffs + // and gateway blips. Without it a single blip surfaces to the user + // as a 502. The retry only fires on transient errors, never on + // timeouts, so it can't mask a genuinely-stuck upstream. + upstream_retry_on_failure: true, upstream_retry_delay: DurationSetting::millis(200), capture_max_body_bytes: 64 * 1024 * 1024, buffer_request_bodies: true, diff --git a/crates/soth-proxy/src/config.rs b/crates/soth-proxy/src/config.rs index d2551945..b9b56039 100644 --- a/crates/soth-proxy/src/config.rs +++ b/crates/soth-proxy/src/config.rs @@ -264,7 +264,10 @@ impl Default for MitmRuntimeConfig { h2_body_idle_timeout_ms: 60_000, h2_response_overflow_mode: H2ResponseOverflowModeConfig::TruncateContinue, upstream_connect_timeout_ms: 5_000, - upstream_retry_on_failure: false, + // On by default (matches the CLI-generated config): one retry for + // transient connect failures during hotspot/gateway blips; never + // retries timeouts. + upstream_retry_on_failure: true, upstream_retry_delay_ms: 200, verify_upstream_tls: true, max_connections_per_host: 64, From bc61cbd01b43c774ed71ab853b8dd7a40298997a Mon Sep 17 00:00:00 2001 From: Apoorv Raj Saxena Date: Sat, 4 Jul 2026 18:03:08 +0400 Subject: [PATCH 2/2] docs: surface upstream_retry keys in soth.example.yaml Follow-up from review: since upstream_retry_on_failure is now on by default, document it (and upstream_retry_delay) in the example config so users can discover the behavior and disable it if they want. No code change. Co-Authored-By: Claude Fable 5 --- soth.example.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/soth.example.yaml b/soth.example.yaml index 71d0d3bd..8a711a35 100644 --- a/soth.example.yaml +++ b/soth.example.yaml @@ -121,6 +121,14 @@ forward_proxy: cache_ttl: "1h" # Certificate cache TTL cache_max: 10000 # Maximum cached certificates + # Upstream connect retry. On by default: a single retry (after + # upstream_retry_delay, bounded by the connect deadline) for transient + # connect failures — refused / unreachable / reset — which are routine + # during hotspot handoffs and gateway blips. Timeouts are never retried, + # so it can't mask a genuinely-stuck upstream. Set to false to disable. + upstream_retry_on_failure: true + upstream_retry_delay: "200ms" + # Connection pool settings pool: max_connections_per_host: 10