fix(sub): pass numeric XMUX counts to Clash reuse-settings (#6843)

Inbound XMUX counts such as maxConnections are stored as JSON numbers, but
buildXhttpClashOpts kept only string values, so the panel default
maxConnections: 3 never reached Clash/Mihomo clients. Numeric values are now
rendered as plain integers (FormatFloat, so 1000000 does not become 1e+06) and
zero values are still dropped.
This commit is contained in:
Murphy Shannon
2026-10-24 10:51:16 +08:00
committed by GitHub
parent 2f62dadd25
commit 864cbd234a
2 changed files with 41 additions and 2 deletions
+9 -2
View File
@@ -1018,8 +1018,15 @@ func buildXhttpClashOpts(xhttp map[string]any) map[string]any {
{"hMaxRequestTimes", "h-max-request-times"},
{"hMaxReusableSecs", "h-max-reusable-secs"},
} {
if v, ok := xmux[f.src].(string); ok && v != "" {
reuse[f.dst] = v
v, ok := nonZeroShareValue(xmux[f.src])
if !ok {
continue
}
if n, isFloat := v.(float64); isFloat {
// JSON numbers decode as float64, and %v would render 1000000 as "1e+06".
reuse[f.dst] = strconv.FormatFloat(n, 'f', -1, 64)
} else {
reuse[f.dst] = fmt.Sprint(v)
}
}
if v, ok := nonZeroShareValue(xmux["hKeepAlivePeriod"]); ok {
+32
View File
@@ -1,6 +1,7 @@
package sub
import (
"encoding/json"
"fmt"
"reflect"
"testing"
@@ -798,6 +799,37 @@ func TestBuildXhttpClashOpts_XmuxMapsToReuseSettings(t *testing.T) {
t.Error("reuse-settings should not appear when only hKeepAlivePeriod=0")
}
})
// Sub-test 5: numeric counts as json.Unmarshal yields them (#6841)
t.Run("NumericXmux", func(t *testing.T) {
cases := []struct {
name, xmux string
want map[string]any
}{
{
name: "PanelDefaults",
xmux: `{"maxConcurrency":"","maxConnections":3,"cMaxReuseTimes":0,"hMaxRequestTimes":"600-900","hMaxReusableSecs":"1800-3000","hKeepAlivePeriod":0}`,
want: map[string]any{"max-connections": "3", "h-max-request-times": "600-900", "h-max-reusable-secs": "1800-3000"},
},
{
name: "LargeCountNotExponent",
xmux: `{"cMaxReuseTimes":1000000}`,
want: map[string]any{"c-max-reuse-times": "1000000"},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
var xmux map[string]any
if err := json.Unmarshal([]byte(tc.xmux), &xmux); err != nil {
t.Fatal(err)
}
opts := buildXhttpClashOpts(map[string]any{"path": "/", "xmux": xmux})
if got := opts["reuse-settings"]; !reflect.DeepEqual(got, tc.want) {
t.Errorf("reuse-settings = %#v, want %#v", got, tc.want)
}
})
}
})
}
func TestBuildXhttpClashOpts_ServerOnlyFieldsExcluded(t *testing.T) {