mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-19 17:40:59 +00:00
fix(sub): deliver vision flow for VLESS+XHTTP+REALITY in share links and Clash (#5232)
The vlessenc fix (#5185) enabled flow on XHTTP only in the security=none branch of genVlessLink, and the Clash builder still gated flow on network==tcp. With XHTTP+REALITY+vlessenc the panel accepts and stores the flow (inboundCanEnableTlsFlow passes), but subscriptions dropped it, so clients received configs without xtls-rprx-vision. Add vlessFlowAllowed mirroring inboundCanEnableTlsFlow — tcp with tls/reality, or xhttp with vlessenc regardless of security layer — and use it in both the vless:// link generator and the Clash proxy builder.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
package sub
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
)
|
||||
|
||||
// Issue #5232: a vision flow set on a VLESS+XHTTP+REALITY (vlessenc) client
|
||||
// must survive into subscription output, not just the inbound JSON.
|
||||
|
||||
const testMlkemEncryption = "mlkem768x25519plus.native.0rtt.dGVzdC1rZXk"
|
||||
|
||||
func TestVlessFlowAllowed(t *testing.T) {
|
||||
enc := map[string]any{"encryption": testMlkemEncryption}
|
||||
noEnc := map[string]any{"encryption": "none"}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
network string
|
||||
security string
|
||||
settings map[string]any
|
||||
want bool
|
||||
}{
|
||||
{"tcp tls", "tcp", "tls", noEnc, true},
|
||||
{"tcp reality", "tcp", "reality", noEnc, true},
|
||||
{"tcp none", "tcp", "none", noEnc, false},
|
||||
{"tcp none vlessenc", "tcp", "none", enc, false},
|
||||
{"xhttp none vlessenc", "xhttp", "none", enc, true},
|
||||
{"xhttp reality vlessenc (#5232)", "xhttp", "reality", enc, true},
|
||||
{"xhttp tls vlessenc", "xhttp", "tls", enc, true},
|
||||
{"xhttp reality no vlessenc", "xhttp", "reality", noEnc, false},
|
||||
{"ws tls", "ws", "tls", noEnc, false},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if got := vlessFlowAllowed(tc.network, tc.security, tc.settings); got != tc.want {
|
||||
t.Fatalf("vlessFlowAllowed(%q, %q, %v) = %v, want %v", tc.network, tc.security, tc.settings, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func flowTestInbound(streamSettings, encryption string) *model.Inbound {
|
||||
return &model.Inbound{
|
||||
Listen: "203.0.113.1",
|
||||
Port: 443,
|
||||
Protocol: model.VLESS,
|
||||
Remark: "flowtest",
|
||||
Settings: `{"clients":[{"id":"11111111-2222-4333-8444-555555555555","email":"user","flow":"xtls-rprx-vision"}],` +
|
||||
`"decryption":"` + encryption + `","encryption":"` + encryption + `"}`,
|
||||
StreamSettings: streamSettings,
|
||||
}
|
||||
}
|
||||
|
||||
const xhttpRealityStream = `{
|
||||
"network": "xhttp",
|
||||
"security": "reality",
|
||||
"xhttpSettings": {"path": "/", "mode": "auto"},
|
||||
"realitySettings": {
|
||||
"serverNames": ["example.com"],
|
||||
"shortIds": ["abcd"],
|
||||
"settings": {"publicKey": "pub", "fingerprint": "chrome"}
|
||||
}
|
||||
}`
|
||||
|
||||
func TestGenVlessLink_FlowXhttpRealityVlessenc(t *testing.T) {
|
||||
s := &SubService{remarkModel: "-ieo"}
|
||||
link := s.genVlessLink(flowTestInbound(xhttpRealityStream, testMlkemEncryption), "user")
|
||||
if !strings.Contains(link, "flow=xtls-rprx-vision") {
|
||||
t.Fatalf("xhttp+reality+vlessenc link must carry the vision flow (#5232), got %q", link)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenVlessLink_NoFlowXhttpRealityWithoutVlessenc(t *testing.T) {
|
||||
s := &SubService{remarkModel: "-ieo"}
|
||||
link := s.genVlessLink(flowTestInbound(xhttpRealityStream, "none"), "user")
|
||||
if strings.Contains(link, "flow=") {
|
||||
t.Fatalf("xhttp+reality without vlessenc must not carry a flow, got %q", link)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenVlessLink_FlowTcpRealityStillWorks(t *testing.T) {
|
||||
stream := `{
|
||||
"network": "tcp",
|
||||
"security": "reality",
|
||||
"tcpSettings": {"header": {"type": "none"}},
|
||||
"realitySettings": {
|
||||
"serverNames": ["example.com"],
|
||||
"shortIds": ["abcd"],
|
||||
"settings": {"publicKey": "pub", "fingerprint": "chrome"}
|
||||
}
|
||||
}`
|
||||
s := &SubService{remarkModel: "-ieo"}
|
||||
link := s.genVlessLink(flowTestInbound(stream, "none"), "user")
|
||||
if !strings.Contains(link, "flow=xtls-rprx-vision") {
|
||||
t.Fatalf("tcp+reality link must keep the vision flow, got %q", link)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user