web_tuic_shutdown_test.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. package web
  2. import (
  3. "crypto/ecdsa"
  4. "crypto/elliptic"
  5. "crypto/rand"
  6. "crypto/x509"
  7. "crypto/x509/pkix"
  8. "encoding/pem"
  9. "fmt"
  10. "math/big"
  11. "net"
  12. "path/filepath"
  13. "strings"
  14. "testing"
  15. "time"
  16. "github.com/mhsanaei/3x-ui/v3/internal/database"
  17. "github.com/mhsanaei/3x-ui/v3/internal/database/dbtest"
  18. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  19. "github.com/mhsanaei/3x-ui/v3/internal/tuic"
  20. "github.com/mhsanaei/3x-ui/v3/internal/web/job"
  21. "github.com/mhsanaei/3x-ui/v3/internal/xray"
  22. )
  23. func TestGracefulShutdownPersistsFinalTuicTraffic(t *testing.T) {
  24. dbDir := t.TempDir()
  25. t.Setenv("XUI_DB_FOLDER", dbDir)
  26. dbtest.InitDB(t, filepath.Join(dbDir, "x-ui.db"))
  27. privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
  28. if err != nil {
  29. t.Fatalf("generate certificate key: %v", err)
  30. }
  31. template := x509.Certificate{
  32. SerialNumber: big.NewInt(1),
  33. Subject: pkix.Name{CommonName: "localhost"},
  34. NotBefore: time.Now().Add(-time.Hour),
  35. NotAfter: time.Now().Add(time.Hour),
  36. IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
  37. }
  38. der, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
  39. if err != nil {
  40. t.Fatalf("create certificate: %v", err)
  41. }
  42. certificate := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
  43. keyDER, err := x509.MarshalECPrivateKey(privateKey)
  44. if err != nil {
  45. t.Fatalf("marshal certificate key: %v", err)
  46. }
  47. privatePEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})
  48. const (
  49. inboundID = 18002
  50. email = "shutdown-tuic@x"
  51. )
  52. inbound := &model.Inbound{
  53. Id: inboundID, Tag: "tuic-shutdown-traffic", Protocol: model.TUIC,
  54. Enable: true, Listen: "127.0.0.1", Port: 0,
  55. Settings: fmt.Sprintf(`{"certificate":%q,"private_key":%q,"clients":[{"uuid":"a0000000-0000-0000-0000-000000000022","password":"p","email":%q,"enable":true}]}`, certificate, privatePEM, email),
  56. }
  57. if err := database.GetDB().Create(inbound).Error; err != nil {
  58. t.Fatalf("create TUIC inbound: %v", err)
  59. }
  60. if err := database.GetDB().Create(&xray.ClientTraffic{InboundId: inboundID, Email: email, Enable: true}).Error; err != nil {
  61. t.Fatalf("create client traffic: %v", err)
  62. }
  63. manager := tuic.GetManager()
  64. manager.StopAll()
  65. manager.CollectAllTraffic()
  66. t.Cleanup(manager.StopAll)
  67. job.NewTuicJob().Run()
  68. if !manager.AddTestTraffic(inboundID, email, 100, 200) {
  69. t.Fatal("TUIC listener did not start")
  70. }
  71. if err := NewServer().Stop(); err != nil && !strings.Contains(err.Error(), "xray is not running") {
  72. t.Fatalf("graceful server shutdown: %v", err)
  73. }
  74. var got xray.ClientTraffic
  75. if err := database.GetDB().Where("email = ?", email).First(&got).Error; err != nil {
  76. t.Fatalf("load persisted client traffic: %v", err)
  77. }
  78. if got.Up != 100 || got.Down != 200 {
  79. t.Fatalf("shutdown persisted traffic (%d,%d), want (100,200)", got.Up, got.Down)
  80. }
  81. _, pending := manager.CollectAllTraffic()
  82. if len(pending) != 0 {
  83. t.Fatalf("final TUIC traffic remains only in manager memory: %+v", pending)
  84. }
  85. }