140 lines
5.9 KiB
Go
140 lines
5.9 KiB
Go
package main
|
|
|
|
import (
|
|
"runtime"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
type XrayNativeTuning struct {
|
|
RuntimeGOMAXPROCS int `json:"runtime_gomaxprocs,omitempty"`
|
|
MuxGlobalSessions int `json:"mux_global_sessions,omitempty"`
|
|
MaxConcurrentConnections int `json:"max_concurrent_connections,omitempty"`
|
|
MaxConcurrentXHTTPRequests int `json:"max_concurrent_xhttp_requests,omitempty"`
|
|
XHTTPMaxSessions int `json:"xhttp_max_sessions,omitempty"`
|
|
TracePackets bool `json:"trace_packets,omitempty"`
|
|
}
|
|
|
|
const (
|
|
defaultNativeRuntimeGOMAXPROCS = 0
|
|
defaultNativeMuxGlobalSessions = 32768
|
|
// Transport sockets, XHTTP requests, and XHTTP sessions are VPN traffic, not
|
|
// website requests. Keep the legacy JSON fields for config compatibility, but
|
|
// always normalize them to unlimited. Actual resource protection is provided by
|
|
// socket/HTTP flow control and the bounded byte queues in xray_xhttp.go.
|
|
defaultNativeMaxConnections = -1
|
|
defaultNativeMaxXHTTPRequests = -1
|
|
|
|
fixedNativeMuxMaxSessions = 64
|
|
fixedNativeMuxUDPIdleMS = 120000
|
|
fixedNativeMuxUDPReadBuffer = 256 * 1024
|
|
fixedNativeMuxUDPWriteBuffer = 256 * 1024
|
|
|
|
defaultNativeXHTTPMaxSessions = -1
|
|
// Packet-up posts are also protected by byte budgets in xray_xhttp.go. Keep
|
|
// the default reorder queue modest so thousands of unauthenticated sessions
|
|
// cannot consume large amounts of memory merely by allocating empty channel
|
|
// buffers. Operators may request more, up to the hard cap enforced there.
|
|
defaultNativeXHTTPBufferedPosts = 64
|
|
|
|
// These are simultaneous resource-safety windows, not request-rate limits.
|
|
// They are deliberately far above the expected 6-8K connected-user load, but
|
|
// finite so a reconnect storm, broken CDN, or hostile client cannot retain an
|
|
// unbounded number of sockets, HTTP handlers, sessions, and goroutine stacks.
|
|
// Transport Accept waits at capacity (kernel backpressure); XHTTP overloads
|
|
// receive 503 rather than the web-rate-limit semantics of 429.
|
|
fixedNativeMaxTransportConnections = 65536
|
|
fixedNativeMaxXHTTPRequests = 65536
|
|
fixedNativeMaxXHTTPSessions = 65536
|
|
fixedNativeHTTP2ConcurrentStreams = 4096
|
|
fixedNativeXHTTPWriteTimeoutMS = 60 * 1000
|
|
|
|
// 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 (
|
|
nativeTuneRuntimeGOMAXPROCS atomic.Int64
|
|
nativeTuneMuxGlobalSessions atomic.Int64
|
|
nativeTuneTracePackets atomic.Bool
|
|
)
|
|
|
|
func init() {
|
|
applyNativeXrayTuning(nil)
|
|
}
|
|
|
|
func normalizeNativeXrayTuning(t *XrayNativeTuning) XrayNativeTuning {
|
|
if t == nil {
|
|
t = &XrayNativeTuning{}
|
|
}
|
|
out := *t
|
|
if out.RuntimeGOMAXPROCS < 0 {
|
|
out.RuntimeGOMAXPROCS = defaultNativeRuntimeGOMAXPROCS
|
|
}
|
|
if out.MuxGlobalSessions <= 0 {
|
|
out.MuxGlobalSessions = defaultNativeMuxGlobalSessions
|
|
}
|
|
// Ignore every old positive/zero admission ceiling. This migration is
|
|
// deliberately unconditional so upgrading an existing server immediately
|
|
// removes the old 4K/8K/32K web-style caps without requiring a panel save.
|
|
out.MaxConcurrentConnections = defaultNativeMaxConnections
|
|
out.MaxConcurrentXHTTPRequests = defaultNativeMaxXHTTPRequests
|
|
out.XHTTPMaxSessions = defaultNativeXHTTPMaxSessions
|
|
return out
|
|
}
|
|
|
|
func applyNativeXrayTuning(t *XrayNativeTuning) XrayNativeTuning {
|
|
out := normalizeNativeXrayTuning(t)
|
|
gomax := out.RuntimeGOMAXPROCS
|
|
if gomax <= 0 {
|
|
gomax = runtime.NumCPU()
|
|
}
|
|
if gomax < 1 {
|
|
gomax = 1
|
|
}
|
|
runtime.GOMAXPROCS(gomax)
|
|
nativeTuneRuntimeGOMAXPROCS.Store(int64(gomax))
|
|
nativeTuneMuxGlobalSessions.Store(int64(out.MuxGlobalSessions))
|
|
nativeTuneTracePackets.Store(out.TracePackets)
|
|
return out
|
|
}
|
|
|
|
func nativeRuntimeGOMAXPROCS() int { return int(nativeTuneRuntimeGOMAXPROCS.Load()) }
|
|
func nativeMuxGlobalSessionLimit() int { return int(nativeTuneMuxGlobalSessions.Load()) }
|
|
func nativeTracePacketsEnabled() bool { return nativeTuneTracePackets.Load() }
|
|
|
|
func nativeMuxMaxSessionLimit() int { return fixedNativeMuxMaxSessions }
|
|
func nativeMuxUDPReadBufferSize() int { return fixedNativeMuxUDPReadBuffer }
|
|
func nativeMuxUDPWriteBufferSize() int { return fixedNativeMuxUDPWriteBuffer }
|
|
func nativeXHTTPBufferedPostLimit() int { return defaultNativeXHTTPBufferedPosts }
|
|
func nativeHTTP2MaxConcurrentStreams() uint32 {
|
|
return fixedNativeHTTP2ConcurrentStreams
|
|
}
|
|
func nativeTransportConnectionLimit() int { return fixedNativeMaxTransportConnections }
|
|
func nativeXHTTPRequestLimit() int { return fixedNativeMaxXHTTPRequests }
|
|
func nativeXHTTPSessionLimit() int { return fixedNativeMaxXHTTPSessions }
|
|
func nativeXHTTPWriteTimeout() time.Duration {
|
|
return fixedNativeXHTTPWriteTimeoutMS * time.Millisecond
|
|
}
|
|
func nativeMuxUDPIdleTimeout() time.Duration {
|
|
return fixedNativeMuxUDPIdleMS * time.Millisecond
|
|
}
|
|
func nativeXHTTPIdleTimeout() time.Duration {
|
|
return fixedNativeXHTTPIdleMS * time.Millisecond
|
|
}
|