Files

349 lines
9.0 KiB
Go

package proxy
import "testing"
func TestClashVlessTransportKeepsTLSFingerprintAndHeaders(t *testing.T) {
src := `
name: vless-ws
type: vless
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000001
tls: true
sni: sni.example.com
skip-cert-verify: true
client-fingerprint: chrome
alpn:
- h2
network: ws
ws-opts:
path: /ray
headers:
Host: cdn.example.com
User-Agent: ant
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
stream := outbound["streamSettings"].(map[string]interface{})
tlsSettings := stream["tlsSettings"].(map[string]interface{})
if _, ok := tlsSettings["allowInsecure"]; ok {
t.Fatalf("tlsSettings must not include deprecated allowInsecure: %#v", tlsSettings)
}
if tlsSettings[xrayTLSInsecurePinKey] != true {
t.Fatalf("tlsSettings missing insecure pin marker: %#v", tlsSettings)
}
if tlsSettings["fingerprint"] != "chrome" {
t.Fatalf("fingerprint = %v, want chrome", tlsSettings["fingerprint"])
}
alpn := tlsSettings["alpn"].([]string)
if len(alpn) != 1 || alpn[0] != "h2" {
t.Fatalf("alpn = %#v, want [h2]", alpn)
}
ws := stream["wsSettings"].(map[string]interface{})
headers := ws["headers"].(map[string]interface{})
if headers["Host"] != "cdn.example.com" || headers["User-Agent"] != "ant" {
t.Fatalf("headers = %#v", headers)
}
}
func TestClashTrojanSkipCertVerifyUsesXrayPinMarker(t *testing.T) {
src := `
name: trojan
type: trojan
server: edge.example.com
port: 443
password: pass
sni: sni.example.com
skip-cert-verify: true
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
stream := outbound["streamSettings"].(map[string]interface{})
tlsSettings := stream["tlsSettings"].(map[string]interface{})
if _, ok := tlsSettings["allowInsecure"]; ok {
t.Fatalf("tlsSettings must not include deprecated allowInsecure: %#v", tlsSettings)
}
if tlsSettings[xrayTLSInsecurePinKey] != true {
t.Fatalf("tlsSettings missing insecure pin marker: %#v", tlsSettings)
}
}
func TestClashVmessGRPCFallbackServiceName(t *testing.T) {
src := `
name: vmess-grpc
type: vmess
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000002
cipher: auto
tls: true
servername: sni.example.com
network: grpc
serviceName: svc
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
stream := outbound["streamSettings"].(map[string]interface{})
grpc := stream["grpcSettings"].(map[string]interface{})
if grpc["serviceName"] != "svc" {
t.Fatalf("grpc serviceName = %v, want svc", grpc["serviceName"])
}
}
func TestClashVmessWSDoesNotEnableBrowserMuxByDefault(t *testing.T) {
src := `
name: vmess-ws
type: vmess
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000004
cipher: auto
network: ws
ws-opts:
path: /ray
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
if _, ok := outbound["mux"]; ok {
t.Fatalf("mux should not be enabled by default after stability validation: %#v", outbound["mux"])
}
}
func TestClashVmessWSEnablesBrowserMuxWhenExplicit(t *testing.T) {
src := `
name: vmess-ws
type: vmess
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000004
cipher: auto
network: ws
browser-mux: true
ws-opts:
path: /ray
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
mux := outbound["mux"].(map[string]interface{})
if mux["enabled"] != true {
t.Fatalf("mux.enabled = %v, want true", mux["enabled"])
}
if mux["concurrency"] != defaultXrayMuxConcurrency {
t.Fatalf("mux.concurrency = %v, want %d", mux["concurrency"], defaultXrayMuxConcurrency)
}
}
func TestClashVlessWSEnablesBrowserMuxWhenExplicit(t *testing.T) {
src := `
name: vless-ws
type: vless
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000005
network: ws
browser-mux: true
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
if outbound["mux"] == nil {
t.Fatalf("expected mux for vless ws outbound")
}
}
func TestClashVmessGRPCDoesNotEnableBrowserMux(t *testing.T) {
src := `
name: vmess-grpc
type: vmess
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000006
cipher: auto
network: grpc
serviceName: svc
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
if _, ok := outbound["mux"]; ok {
t.Fatalf("grpc outbound must not enable browser mux by default: %#v", outbound["mux"])
}
}
func TestClashVmessWSAllowsExplicitMuxDisable(t *testing.T) {
src := `
name: vmess-ws
type: vmess
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000007
cipher: auto
network: ws
browser-mux: false
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
if _, ok := outbound["mux"]; ok {
t.Fatalf("explicit mux disable should be respected: %#v", outbound["mux"])
}
}
func TestClashVmessWSAllowsNestedMuxDisable(t *testing.T) {
src := `
name: vmess-ws
type: vmess
server: edge.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000008
cipher: auto
network: ws
mux:
enabled: false
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
if _, ok := outbound["mux"]; ok {
t.Fatalf("nested mux disable should be respected: %#v", outbound["mux"])
}
}
func TestSingBoxHysteria2ClashKeepsTLSFingerprintAndCongestion(t *testing.T) {
src := `
type: hysteria2
server: hy.example.com
port: 443
ports: 20000-50000, 51000-52000
password: test-password
sni: sni.example.com
alpn:
- h3
client-fingerprint: chrome
congestion-control: bbr
`
out, err := BuildSingBoxOutbound(src)
if err != nil {
t.Fatalf("BuildSingBoxOutbound returned error: %v", err)
}
if out["congestion_control"] != "bbr" {
t.Fatalf("congestion_control = %v, want bbr", out["congestion_control"])
}
serverPorts, ok := out["server_ports"].(string)
if !ok {
t.Fatalf("server_ports is %T, want string", out["server_ports"])
}
if serverPorts != "20000:50000,51000:52000" {
t.Fatalf("server_ports = %#v, want converted ranges", serverPorts)
}
tls := out["tls"].(map[string]interface{})
alpn := tls["alpn"].([]string)
if len(alpn) != 1 || alpn[0] != "h3" {
t.Fatalf("tls.alpn = %#v, want [h3]", alpn)
}
utls := tls["utls"].(map[string]interface{})
if utls["fingerprint"] != "chrome" {
t.Fatalf("utls fingerprint = %v, want chrome", utls["fingerprint"])
}
}
func TestSingBoxTUICClashKeepsServernameFingerprintAndCongestion(t *testing.T) {
src := `
type: tuic
server: tuic.example.com
port: 443
uuid: 00000000-0000-0000-0000-000000000003
password: test-password
servername: sni.example.com
client-fingerprint: chrome
congestion-controller: cubic
`
out, err := BuildSingBoxOutbound(src)
if err != nil {
t.Fatalf("BuildSingBoxOutbound returned error: %v", err)
}
if out["congestion_control"] != "cubic" {
t.Fatalf("congestion_control = %v, want cubic", out["congestion_control"])
}
tls := out["tls"].(map[string]interface{})
if tls["server_name"] != "sni.example.com" {
t.Fatalf("tls.server_name = %v, want sni.example.com", tls["server_name"])
}
utls := tls["utls"].(map[string]interface{})
if utls["fingerprint"] != "chrome" {
t.Fatalf("utls fingerprint = %v, want chrome", utls["fingerprint"])
}
}
func TestClashSSBuildsXrayServersArray(t *testing.T) {
src := `
name: ss-node
type: ss
server: ss.example.com
port: 8388
cipher: aes-128-gcm
password: test-password
`
_, outbound, err := ParseProxyNode(src)
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
if outbound["protocol"] != "shadowsocks" {
t.Fatalf("protocol = %v, want shadowsocks", outbound["protocol"])
}
settings := outbound["settings"].(map[string]interface{})
servers, ok := settings["servers"].([]interface{})
if !ok || len(servers) != 1 {
t.Fatalf("servers invalid: %#v", settings["servers"])
}
server := servers[0].(map[string]interface{})
if server["address"] != "ss.example.com" || server["port"] != 8388 || server["method"] != "aes-128-gcm" || server["password"] != "test-password" {
t.Fatalf("server invalid: %#v", server)
}
if _, ok := settings["address"]; ok {
t.Fatalf("legacy flat shadowsocks settings should not be present: %#v", settings)
}
}
func TestSSURIBuildsXrayServersArray(t *testing.T) {
_, outbound, err := ParseProxyNode("ss://YWVzLTEyOC1nY206cGFzc3dvcmQ@example.com:8388")
if err != nil {
t.Fatalf("ParseProxyNode returned error: %v", err)
}
settings := outbound["settings"].(map[string]interface{})
servers, ok := settings["servers"].([]interface{})
if !ok || len(servers) != 1 {
t.Fatalf("servers invalid: %#v", settings["servers"])
}
server := servers[0].(map[string]interface{})
if server["address"] != "example.com" || server["port"] != 8388 || server["method"] != "aes-128-gcm" || server["password"] != "password" {
t.Fatalf("server invalid: %#v", server)
}
}