package v4_test import ( "encoding/json" "reflect" "testing" "github.com/golang/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/app/dispatcher" "github.com/v2fly/v2ray-core/v5/app/log" "github.com/v2fly/v2ray-core/v5/app/proxyman" "github.com/v2fly/v2ray-core/v5/app/router" "github.com/v2fly/v2ray-core/v5/app/router/routercommon" "github.com/v2fly/v2ray-core/v5/common" clog "github.com/v2fly/v2ray-core/v5/common/log" "github.com/v2fly/v2ray-core/v5/common/net" "github.com/v2fly/v2ray-core/v5/common/protocol" "github.com/v2fly/v2ray-core/v5/common/serial" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/muxcfg" "github.com/v2fly/v2ray-core/v5/infra/conf/cfgcommon/testassist" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/memconservative" _ "github.com/v2fly/v2ray-core/v5/infra/conf/geodata/standard" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" "github.com/v2fly/v2ray-core/v5/proxy/blackhole" dns_proxy "github.com/v2fly/v2ray-core/v5/proxy/dns" "github.com/v2fly/v2ray-core/v5/proxy/freedom" "github.com/v2fly/v2ray-core/v5/proxy/vmess" "github.com/v2fly/v2ray-core/v5/proxy/vmess/inbound" "github.com/v2fly/v2ray-core/v5/transport/internet" "github.com/v2fly/v2ray-core/v5/transport/internet/http" "github.com/v2fly/v2ray-core/v5/transport/internet/tls" "github.com/v2fly/v2ray-core/v5/transport/internet/websocket" ) func TestV2RayConfig(t *testing.T) { createParser := func() func(string) (proto.Message, error) { return func(s string) (proto.Message, error) { config := new(v4.Config) if err := json.Unmarshal([]byte(s), config); err != nil { return nil, err } return config.Build() } } testassist.RunMultiTestCase(t, []testassist.TestCase{ { Input: `{ "outbound": { "protocol": "freedom", "settings": {} }, "log": { "access": "/var/log/v2ray/access.log", "loglevel": "error", "error": "/var/log/v2ray/error.log" }, "inbound": { "streamSettings": { "network": "ws", "wsSettings": { "headers": { "host": "example.domain" }, "path": "" }, "tlsSettings": { "alpn": "h2" }, "security": "tls" }, "protocol": "vmess", "port": 443, "settings": { "clients": [ { "alterId": 100, "security": "aes-128-gcm", "id": "0cdf8a45-303d-4fed-9780-29aa7f54175e" } ] } }, "inbounds": [{ "streamSettings": { "network": "ws", "wsSettings": { "headers": { "host": "example.domain" }, "path": "" }, "tlsSettings": { "alpn": "h2" }, "security": "tls" }, "protocol": "vmess", "port": "443-500", "allocate": { "strategy": "random", "concurrency": 3 }, "settings": { "clients": [ { "alterId": 100, "security": "aes-128-gcm", "id": "0cdf8a45-303d-4fed-9780-29aa7f54175e" } ] } }], "outboundDetour": [ { "tag": "blocked", "protocol": "blackhole" }, { "protocol": "dns" } ], "routing": { "strategy": "rules", "settings": { "rules": [ { "ip": [ "10.0.0.0/8" ], "type": "field", "outboundTag": "blocked" } ] } }, "transport": { "httpSettings": { "path": "/test" } } }`, Parser: createParser(), Output: &core.Config{ App: []*anypb.Any{ serial.ToTypedMessage(&log.Config{ Error: &log.LogSpecification{ Type: log.LogType_File, Level: clog.Severity_Error, Path: "/var/log/v2ray/error.log", }, Access: &log.LogSpecification{ Type: log.LogType_File, Path: "/var/log/v2ray/access.log", }, }), serial.ToTypedMessage(&dispatcher.Config{}), serial.ToTypedMessage(&proxyman.InboundConfig{}), serial.ToTypedMessage(&proxyman.OutboundConfig{}), serial.ToTypedMessage(&router.Config{ DomainStrategy: router.DomainStrategy_AsIs, Rule: []*router.RoutingRule{ { Geoip: []*routercommon.GeoIP{ { Cidr: []*routercommon.CIDR{ { Ip: []byte{10, 0, 0, 0}, Prefix: 8, }, }, }, }, TargetTag: &router.RoutingRule_Tag{ Tag: "blocked", }, }, }, }), }, Outbound: []*core.OutboundHandlerConfig{ { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "tcp", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&freedom.Config{ DomainStrategy: freedom.Config_AS_IS, UserLevel: 0, }), }, { Tag: "blocked", SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "tcp", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&blackhole.Config{}), }, { SenderSettings: serial.ToTypedMessage(&proxyman.SenderConfig{ StreamSettings: &internet.StreamConfig{ ProtocolName: "tcp", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, }, }), ProxySettings: serial.ToTypedMessage(&dns_proxy.Config{ Server: &net.Endpoint{}, }), }, }, Inbound: []*core.InboundHandlerConfig{ { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{ From: 443, To: 443, }, StreamSettings: &internet.StreamConfig{ ProtocolName: "websocket", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "websocket", Settings: serial.ToTypedMessage(&websocket.Config{ Header: []*websocket.Header{ { Key: "host", Value: "example.domain", }, }, }), }, { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, SecurityType: "v2ray.core.transport.internet.tls.Config", SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ NextProtocol: []string{"h2"}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Level: 0, Account: serial.ToTypedMessage(&vmess.Account{ Id: "0cdf8a45-303d-4fed-9780-29aa7f54175e", AlterId: 100, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }), }, { ReceiverSettings: serial.ToTypedMessage(&proxyman.ReceiverConfig{ PortRange: &net.PortRange{ From: 443, To: 500, }, AllocationStrategy: &proxyman.AllocationStrategy{ Type: proxyman.AllocationStrategy_Random, Concurrency: &proxyman.AllocationStrategy_AllocationStrategyConcurrency{ Value: 3, }, }, StreamSettings: &internet.StreamConfig{ ProtocolName: "websocket", TransportSettings: []*internet.TransportConfig{ { ProtocolName: "websocket", Settings: serial.ToTypedMessage(&websocket.Config{ Header: []*websocket.Header{ { Key: "host", Value: "example.domain", }, }, }), }, { ProtocolName: "http", Settings: serial.ToTypedMessage(&http.Config{ Path: "/test", }), }, }, SecurityType: "v2ray.core.transport.internet.tls.Config", SecuritySettings: []*anypb.Any{ serial.ToTypedMessage(&tls.Config{ NextProtocol: []string{"h2"}, }), }, }, }), ProxySettings: serial.ToTypedMessage(&inbound.Config{ User: []*protocol.User{ { Level: 0, Account: serial.ToTypedMessage(&vmess.Account{ Id: "0cdf8a45-303d-4fed-9780-29aa7f54175e", AlterId: 100, SecuritySettings: &protocol.SecurityConfig{ Type: protocol.SecurityType_AES128_GCM, }, }), }, }, }), }, }, }, }, }) } func TestMuxConfig_Build(t *testing.T) { tests := []struct { name string fields string want *proxyman.MultiplexingConfig }{ {"default", `{"enabled": true, "concurrency": 16}`, &proxyman.MultiplexingConfig{ Enabled: true, Concurrency: 16, }}, {"empty def", `{}`, &proxyman.MultiplexingConfig{ Enabled: false, Concurrency: 8, }}, {"not enable", `{"enabled": false, "concurrency": 4}`, &proxyman.MultiplexingConfig{ Enabled: false, Concurrency: 4, }}, {"forbidden", `{"enabled": false, "concurrency": -1}`, nil}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &muxcfg.MuxConfig{} common.Must(json.Unmarshal([]byte(tt.fields), m)) if got := m.Build(); !reflect.DeepEqual(got, tt.want) { t.Errorf("MuxConfig.Build() = %v, want %v", got, tt.want) } }) } }