json_flow_gate_test.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. package sub
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database"
  7. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  8. )
  9. func seedFlowInbound(t *testing.T, subId, tag string, port int, stream string) *model.Inbound {
  10. t.Helper()
  11. db := database.GetDB()
  12. uuid := "11111111-2222-4333-8444-" + fmt.Sprintf("%012d", port)
  13. email := tag + "@e"
  14. settings := fmt.Sprintf(
  15. `{"clients":[{"id":%q,"email":%q,"subId":%q,"enable":true,"flow":"xtls-rprx-vision"}],"decryption":"none"}`,
  16. uuid, email, subId)
  17. ib := &model.Inbound{
  18. UserId: 1, Tag: tag, Enable: true, Listen: "203.0.113.5", Port: port,
  19. Protocol: model.VLESS, Remark: tag, Settings: settings, StreamSettings: stream,
  20. SubSortIndex: 1,
  21. }
  22. if err := db.Create(ib).Error; err != nil {
  23. t.Fatalf("seed inbound %s: %v", tag, err)
  24. }
  25. client := &model.ClientRecord{Email: email, SubID: subId, UUID: uuid, Enable: true}
  26. if err := db.Create(client).Error; err != nil {
  27. t.Fatalf("seed client %s: %v", email, err)
  28. }
  29. link := &model.ClientInbound{ClientId: client.Id, InboundId: ib.Id, FlowOverride: "xtls-rprx-vision"}
  30. if err := db.Create(link).Error; err != nil {
  31. t.Fatalf("seed client_inbound %s: %v", email, err)
  32. }
  33. return ib
  34. }
  35. // A vision flow left on a client after its inbound moved to a transport Vision
  36. // cannot use is stripped from the raw link and the Clash proxy; the JSON
  37. // subscription must agree instead of emitting an outbound xray rejects.
  38. func TestSub_JSONStripsFlowOnUnsupportedTransport(t *testing.T) {
  39. seedSubDB(t)
  40. seedFlowInbound(t, "s1", "wsflow", 4601, wsTLSStream)
  41. links, _, _, _, err := NewSubService("").GetSubs("s1", "req.example.com")
  42. if err != nil {
  43. t.Fatalf("GetSubs: %v", err)
  44. }
  45. if joined := strings.Join(links, "\n"); strings.Contains(joined, "flow=") {
  46. t.Fatalf("raw link must not carry a flow on ws+tls: %s", joined)
  47. }
  48. clash := NewSubClashService(false, "", NewSubService(""))
  49. yaml, _, err := clash.GetClash("s1", "req.example.com")
  50. if err != nil {
  51. t.Fatalf("GetClash: %v", err)
  52. }
  53. if strings.Contains(yaml, "flow:") {
  54. t.Fatalf("clash proxy must not carry a flow on ws+tls:\n%s", yaml)
  55. }
  56. js := NewSubJsonService("", "", "", NewSubService(""))
  57. out, _, err := js.GetJson("s1", "req.example.com", false)
  58. if err != nil {
  59. t.Fatalf("GetJson: %v", err)
  60. }
  61. if strings.Contains(out, `"flow"`) {
  62. t.Fatalf("json outbound must not carry a flow on ws+tls:\n%s", out)
  63. }
  64. }
  65. // The gate must not strip a flow the transport does support.
  66. func TestSub_JSONKeepsFlowOnTcpTLS(t *testing.T) {
  67. seedSubDB(t)
  68. seedFlowInbound(t, "s1", "tcpflow", 4602,
  69. `{"network":"tcp","security":"tls","tlsSettings":{"serverName":"base.sni"}}`)
  70. js := NewSubJsonService("", "", "", NewSubService(""))
  71. out, _, err := js.GetJson("s1", "req.example.com", false)
  72. if err != nil {
  73. t.Fatalf("GetJson: %v", err)
  74. }
  75. if !strings.Contains(out, `"flow": "xtls-rprx-vision"`) {
  76. t.Fatalf("json outbound must keep the vision flow on tcp+tls:\n%s", out)
  77. }
  78. }