From 7c51ea3f86df9730a333c8e8937d0827fd10ffcc Mon Sep 17 00:00:00 2001 From: penguinehis Date: Wed, 22 Jul 2026 17:47:51 -0300 Subject: [PATCH] fix 502 2 --- connection_lifetime_test.go | 15 ++++++++++++--- xray_native_tuning.go | 23 +++++++++++++++++------ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/connection_lifetime_test.go b/connection_lifetime_test.go index 37e18d0..e35617b 100644 --- a/connection_lifetime_test.go +++ b/connection_lifetime_test.go @@ -19,8 +19,17 @@ func TestSSHIdleTimeoutExplicitValue(t *testing.T) { } } -func TestNativeXHTTPConnectedIdleSweepDisabled(t *testing.T) { - if got := nativeXHTTPIdleTimeout(); got != 0 { - t.Fatalf("native XHTTP idle timeout = %s, want disabled", got) +// The connected-session sweeper is the backstop that reaps XHTTP->SSH sessions +// whose stream-down GET context never fires (silent client drop behind a CDN). +// Without it those sessions leak fds/goroutines until a process restart, which +// is what produced the recurring reboot-only XHTTP 502s. It must stay enabled; +// the window is generous so only zero-traffic (dead) sessions are reaped. +func TestNativeXHTTPConnectedIdleSweepEnabled(t *testing.T) { + got := nativeXHTTPIdleTimeout() + if got <= 0 { + t.Fatalf("native XHTTP idle timeout = %s, want a positive backstop window", got) + } + if got != 20*time.Minute { + t.Fatalf("native XHTTP idle timeout = %s, want 20m backstop", got) } } diff --git a/xray_native_tuning.go b/xray_native_tuning.go index 9293118..2798e43 100644 --- a/xray_native_tuning.go +++ b/xray_native_tuning.go @@ -37,12 +37,23 @@ const ( // buffers. Operators may request more, up to the hard cap enforced there. defaultNativeXHTTPBufferedPosts = 64 - // Do not impose an application-level lifetime on a connected XHTTP VPN - // session. The official Xray server keeps a connected session for the - // lifetime of its stream-down GET; request cancellation and I/O errors own - // cleanup. A fixed five-minute sweeper incorrectly killed healthy but idle - // VPNs. Zero disables the connected-session sweeper. - fixedNativeXHTTPIdleMS = 0 + // Backstop reaper for connected XHTTP VPN sessions. The stream-down GET's + // request context is the primary lifetime owner, but behind a CDN that context + // frequently never fires when a client silently drops (mobile networks, CDN + // connection pooling, half-open TCP). When it doesn't, an idle SSH backend + // never errors either, so the session, its goroutines, socket/fd, and SSH + // connection leak until the whole process restarts. That accumulation is what + // drove the recurring XHTTP 502s that only a reboot cleared: the origin slowly + // ran out of fds/memory and could no longer serve new stream-down GETs. + // + // This sweeper only ever reaps sessions with genuinely stale lastSeen. lastSeen + // is refreshed on every successful read OR write via nativeXHTTPConn.onActivity, + // so any tunnel still passing data or keepalives is never touched -- only a + // session with zero bytes in BOTH directions for the full window (i.e. one that + // looks dead) is closed. 20 minutes is generous enough not to disturb a + // genuinely idle-but-alive tunnel while still bounding resource growth under + // heavy 6-8K-user churn. Zero disables the connected-session sweeper. + fixedNativeXHTTPIdleMS = 20 * 60 * 1000 ) var (