| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- package sub
- import (
- "net/url"
- "reflect"
- "strings"
- "testing"
- "github.com/mhsanaei/3x-ui/v3/internal/database/model"
- )
- func TestGenTuicLinkBasic(t *testing.T) {
- inbound := &model.Inbound{
- Listen: "198.51.100.1",
- Port: 8443,
- Protocol: model.TUIC,
- Remark: "tuic-test",
- Settings: `{"server":{"certificate":"/path/cert","private_key":"/path/key"},"clients":[{"uuid":"11111111-1111-1111-1111-111111111111","password":"testpassword","email":"user@test"}]}`,
- }
- s := &SubService{}
- link := s.genTuicLink(inbound, "user@test")
- u, err := url.Parse(link)
- if err != nil {
- t.Fatalf("link does not parse: %v\ngot: %s", err, link)
- }
- if u.Scheme != "tuic" {
- t.Fatalf("scheme = %q, want tuic", u.Scheme)
- }
- if u.Host != "198.51.100.1:8443" {
- t.Fatalf("host = %q, want 198.51.100.1:8443", u.Host)
- }
- if u.User.Username() != "11111111-1111-1111-1111-111111111111" {
- t.Fatalf("username = %q, want uuid", u.User.Username())
- }
- pass, ok := u.User.Password()
- if !ok || pass != "testpassword" {
- t.Fatalf("password = %q, want testpassword", pass)
- }
- q := u.Query()
- if q.Get("congestion_control") != "bbr" {
- t.Fatalf("congestion_control = %q, want bbr", q.Get("congestion_control"))
- }
- if q.Get("allow_insecure") != "0" {
- t.Fatalf("allow_insecure = %q, want 0", q.Get("allow_insecure"))
- }
- if q.Get("alpn") != "h3,spdy/3.1" {
- t.Fatalf("alpn = %q, want h3,spdy/3.1", q.Get("alpn"))
- }
- if q.Get("udp_relay_mode") != "native" {
- t.Fatalf("udp_relay_mode = %q, want native", q.Get("udp_relay_mode"))
- }
- if u.Fragment != "tuic-test-user@test" {
- t.Fatalf("fragment = %q, want tuic-test-user@test", u.Fragment)
- }
- }
- func TestGenTuicLinkExplicitParams(t *testing.T) {
- inbound := &model.Inbound{
- Listen: "198.51.100.1",
- Port: 8443,
- Protocol: model.TUIC,
- Remark: "tuic-test",
- Settings: `{"server":{"certificate":"/path/cert","private_key":"/path/key","congestion_control":"cubic","alpn":["h3"],"sni":"tuic.example.com","udp_relay_mode":"quic"},"clients":[{"uuid":"11111111-1111-1111-1111-111111111111","password":"testpassword","email":"user@test"}]}`,
- }
- s := &SubService{}
- link := s.genTuicLink(inbound, "user@test")
- u, err := url.Parse(link)
- if err != nil {
- t.Fatalf("link does not parse: %v\ngot: %s", err, link)
- }
- q := u.Query()
- if q.Get("congestion_control") != "cubic" {
- t.Fatalf("congestion_control = %q, want cubic", q.Get("congestion_control"))
- }
- if q.Get("alpn") != "h3" {
- t.Fatalf("alpn = %q, want h3", q.Get("alpn"))
- }
- if q.Get("sni") != "tuic.example.com" {
- t.Fatalf("sni = %q, want tuic.example.com", q.Get("sni"))
- }
- if q.Get("udp_relay_mode") != "quic" {
- t.Fatalf("udp_relay_mode = %q, want quic", q.Get("udp_relay_mode"))
- }
- }
- func TestGenTuicLinkExternalProxyFanOut(t *testing.T) {
- inbound := &model.Inbound{
- Listen: "0.0.0.0",
- Port: 8443,
- Protocol: model.TUIC,
- Remark: "tuic-base",
- Settings: `{"server":{"certificate":"/path/cert","private_key":"/path/key"},"clients":[{"uuid":"11111111-1111-1111-1111-111111111111","password":"testpassword","email":"user@test"}]}`,
- StreamSettings: `{"externalProxy":[{"dest":"host1.example.com","port":9443,"remark":"US"},{"dest":"host2.example.com","port":10443,"remark":"EU","allowInsecure":true}]}`,
- }
- s := &SubService{}
- links := s.genTuicLink(inbound, "user@test")
- lines := strings.Split(strings.TrimSpace(links), "\n")
- if len(lines) != 2 {
- t.Fatalf("expected 2 links for externalProxy fan-out, got %d:\n%s", len(lines), links)
- }
- u1, err := url.Parse(lines[0])
- if err != nil {
- t.Fatalf("first link does not parse: %v", err)
- }
- if u1.Host != "host1.example.com:9443" {
- t.Fatalf("first host = %q, want host1.example.com:9443", u1.Host)
- }
- if !strings.Contains(u1.Fragment, "US") {
- t.Fatalf("first fragment = %q, want to contain US", u1.Fragment)
- }
- if u1.Query().Get("allow_insecure") != "0" {
- t.Fatalf("first allow_insecure = %q, want 0", u1.Query().Get("allow_insecure"))
- }
- if u1.Query().Has("allowInsecure") {
- t.Fatalf("first link should not have allowInsecure")
- }
- u2, err := url.Parse(lines[1])
- if err != nil {
- t.Fatalf("second link does not parse: %v", err)
- }
- if u2.Host != "host2.example.com:10443" {
- t.Fatalf("second host = %q, want host2.example.com:10443", u2.Host)
- }
- if !strings.Contains(u2.Fragment, "EU") {
- t.Fatalf("second fragment = %q, want to contain EU", u2.Fragment)
- }
- if u2.Query().Get("allow_insecure") != "1" {
- t.Fatalf("second allow_insecure = %q, want 1", u2.Query().Get("allow_insecure"))
- }
- if u2.Query().Has("allowInsecure") {
- t.Fatalf("second link should not have allowInsecure")
- }
- }
- func TestBuildTuicProxy_ExternalProxyOverrides(t *testing.T) {
- svc := &SubClashService{SubService: &SubService{address: "sub.example.com"}}
- inbound := &model.Inbound{
- Listen: "198.51.100.1",
- Port: 8443,
- Protocol: model.TUIC,
- Remark: "tuic-base",
- Settings: `{"server":{"certificate":"/path/cert","private_key":"/path/key","sni":"base.example.com","alpn":["h3"]},"clients":[{"uuid":"11111111-1111-1111-1111-111111111111","password":"testpassword","email":"user@test"}]}`,
- }
- client := model.Client{Email: "user@test"}
- // 1. Without overrides from ep
- baseProxy := svc.buildTuicProxy(svc.SubService, inbound, client, nil)
- if baseProxy == nil {
- t.Fatal("baseProxy is nil")
- }
- if baseProxy["sni"] != "base.example.com" {
- t.Fatalf("base sni = %v, want base.example.com", baseProxy["sni"])
- }
- if !reflect.DeepEqual(baseProxy["alpn"], []string{"h3"}) {
- t.Fatalf("base alpn = %v, want [h3]", baseProxy["alpn"])
- }
- if _, ok := baseProxy["skip-cert-verify"]; ok {
- t.Fatalf("base skip-cert-verify should not be set")
- }
- // 2. With overrides from ep
- ep := map[string]any{
- "dest": "custom.example.com",
- "port": float64(9443),
- "remark": "custom-node",
- "sni": "custom.sni.com",
- "alpn": "h3,spdy/3.1",
- "allowInsecure": true,
- }
- proxy := svc.buildTuicProxy(svc.SubService, inbound, client, ep)
- if proxy == nil {
- t.Fatal("proxy is nil")
- }
- if proxy["sni"] != "custom.sni.com" {
- t.Fatalf("sni = %v, want custom.sni.com", proxy["sni"])
- }
- if !reflect.DeepEqual(proxy["alpn"], []string{"h3", "spdy/3.1"}) {
- t.Fatalf("alpn = %v, want [h3 spdy/3.1]", proxy["alpn"])
- }
- if proxy["skip-cert-verify"] != true {
- t.Fatalf("skip-cert-verify = %v, want true", proxy["skip-cert-verify"])
- }
- }
|