sub_panic_test.go 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. package sub
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/gin-gonic/gin"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database"
  7. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  8. )
  9. // A subscription is built by iterating every client's share link with no
  10. // recover(), so any panic in the link generators 500s the whole subscription
  11. // for every client. Valid-but-unusual stream settings (an empty Reality
  12. // shortIds/serverNames array, a tcp-http header with no request, a grpc block
  13. // missing its keys) must therefore produce a link, not a panic.
  14. func TestGetSubsToleratesUnusualStreamSettings(t *testing.T) {
  15. gin.SetMode(gin.TestMode)
  16. cases := []struct {
  17. name string
  18. stream string
  19. }{
  20. {"reality empty arrays", `{"network":"tcp","security":"reality","realitySettings":{"serverNames":[],"shortIds":[],"settings":{"publicKey":"pk"}}}`},
  21. {"tcp http missing request", `{"network":"tcp","security":"none","tcpSettings":{"header":{"type":"http","response":{"headers":{}}}}}`},
  22. {"tcp http empty path", `{"network":"tcp","security":"none","tcpSettings":{"header":{"type":"http","request":{"path":[]}}}}`},
  23. {"grpc missing keys", `{"network":"grpc","security":"none","grpcSettings":{}}`},
  24. {"empty stream settings", `{}`},
  25. }
  26. for i, tc := range cases {
  27. t.Run(tc.name, func(t *testing.T) {
  28. seedSubDB(t)
  29. subId := fmt.Sprintf("s%d", i)
  30. seedSubInbound(t, subId, fmt.Sprintf("t%d", i), 46000+i, 1, tc.stream)
  31. links, _, _, _, err := NewSubService("").GetSubs(subId, "req.example.com")
  32. if err != nil {
  33. t.Fatalf("GetSubs errored: %v", err)
  34. }
  35. if len(links) != 1 {
  36. t.Fatalf("expected 1 share link, got %d", len(links))
  37. }
  38. })
  39. }
  40. }
  41. // The JSON subscription generator for a Hysteria inbound whose StreamSettings
  42. // omit the hysteriaSettings key must not panic (which would 500 the whole JSON
  43. // subscription); the raw generator already tolerates this shape.
  44. func TestGetJsonToleratesHysteriaWithoutHysteriaSettings(t *testing.T) {
  45. seedSubDB(t)
  46. db := database.GetDB()
  47. const subId = "hy1"
  48. const email = "hy@e"
  49. ib := &model.Inbound{
  50. UserId: 1, Tag: "hy", Enable: true, Listen: "203.0.113.5", Port: 46200,
  51. Protocol: model.Hysteria,
  52. Remark: "hy",
  53. Settings: fmt.Sprintf(`{"version":2,"clients":[{"auth":"hyauth","email":%q,"subId":%q,"enable":true}]}`, email, subId),
  54. StreamSettings: `{"security":"tls","tlsSettings":{"serverName":"hy.sni"}}`,
  55. }
  56. if err := db.Create(ib).Error; err != nil {
  57. t.Fatalf("seed inbound: %v", err)
  58. }
  59. client := &model.ClientRecord{Email: email, SubID: subId, Enable: true}
  60. if err := db.Create(client).Error; err != nil {
  61. t.Fatalf("seed client: %v", err)
  62. }
  63. if err := db.Create(&model.ClientInbound{ClientId: client.Id, InboundId: ib.Id}).Error; err != nil {
  64. t.Fatalf("seed client_inbound: %v", err)
  65. }
  66. jsonService := NewSubJsonService("", "", "", NewSubService(""))
  67. out, _, err := jsonService.GetJson(subId, "sub.example.com", true)
  68. if err != nil {
  69. t.Fatalf("GetJson: %v", err)
  70. }
  71. if out == "" {
  72. t.Fatal("GetJson returned empty for a hysteria inbound without hysteriaSettings")
  73. }
  74. }