happ.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package sub
  2. import (
  3. "regexp"
  4. "strings"
  5. "github.com/gin-gonic/gin"
  6. )
  7. var happUserAgentRegex = regexp.MustCompile(`(?i)\bhapp\b`)
  8. // HappConfig holds all Happ client customization parameters.
  9. type HappConfig struct {
  10. AutoDetect bool
  11. ProviderId string
  12. NewUrl string
  13. FallbackUrl string
  14. SubInfoColor string
  15. SubInfoText string
  16. SubInfoButtonText string
  17. SubInfoButtonLink string
  18. SubExpire bool
  19. SubExpireButtonLink string
  20. NotificationExpire bool
  21. NoLimit bool
  22. AlwaysHwid bool
  23. TunMode string
  24. TunType string
  25. ExcludeRoutes string
  26. ExcludeApns bool
  27. ColorProfile string
  28. PingType string
  29. AutoConnect bool
  30. AutoConnectType string
  31. PerAppMode string
  32. PerAppList string
  33. }
  34. // IsHappClient checks if the client user-agent identifies as Happ.
  35. func IsHappClient(userAgent string) bool {
  36. return happUserAgentRegex.MatchString(userAgent)
  37. }
  38. // ApplyHappHeaders sets standard and advanced Happ subscription headers.
  39. func ApplyHappHeaders(c *gin.Context, cfg HappConfig, isHapp bool) {
  40. if c == nil || c.Writer == nil || !cfg.AutoDetect || !isHapp {
  41. return
  42. }
  43. if cfg.ProviderId != "" {
  44. c.Writer.Header().Set("ProviderID", cfg.ProviderId)
  45. }
  46. if cfg.NewUrl != "" {
  47. c.Writer.Header().Set("New-Url", cfg.NewUrl)
  48. }
  49. if cfg.FallbackUrl != "" {
  50. c.Writer.Header().Set("Fallback-Url", cfg.FallbackUrl)
  51. }
  52. if text := strings.TrimSpace(cfg.SubInfoText); text != "" {
  53. color := strings.TrimSpace(cfg.SubInfoColor)
  54. switch strings.ToLower(color) {
  55. case "primary", "info":
  56. color = "blue"
  57. case "success":
  58. color = "green"
  59. case "warning", "danger":
  60. color = "red"
  61. case "":
  62. color = "blue"
  63. }
  64. c.Writer.Header().Set("Sub-Info-Color", color)
  65. c.Writer.Header().Set("Sub-Info-Text", text)
  66. if btnText := strings.TrimSpace(cfg.SubInfoButtonText); btnText != "" {
  67. c.Writer.Header().Set("Sub-Info-Button-Text", btnText)
  68. }
  69. if btnLink := strings.TrimSpace(cfg.SubInfoButtonLink); btnLink != "" {
  70. c.Writer.Header().Set("Sub-Info-Button-Link", btnLink)
  71. }
  72. }
  73. if cfg.SubExpire {
  74. c.Writer.Header().Set("Sub-Expire", "1")
  75. if link := strings.TrimSpace(cfg.SubExpireButtonLink); link != "" {
  76. c.Writer.Header().Set("Sub-Expire-Button-Link", link)
  77. }
  78. }
  79. if cfg.NotificationExpire {
  80. c.Writer.Header().Set("Notification-Subs-Expire", "1")
  81. }
  82. if cfg.NoLimit {
  83. c.Writer.Header().Set("No-Limit-Enabled", "1")
  84. }
  85. if cfg.AlwaysHwid {
  86. c.Writer.Header().Set("Subscription-Always-Hwid-Enable", "1")
  87. }
  88. if cfg.TunMode != "" {
  89. c.Writer.Header().Set("Tun-Mode", cfg.TunMode)
  90. }
  91. if cfg.TunType != "" {
  92. c.Writer.Header().Set("Tun-Type", cfg.TunType)
  93. }
  94. if routes := strings.TrimSpace(cfg.ExcludeRoutes); routes != "" {
  95. c.Writer.Header().Set("Exclude-Routes", routes)
  96. }
  97. if cfg.ExcludeApns {
  98. c.Writer.Header().Set("Exclude-Apns-Enable", "true")
  99. }
  100. if profile := strings.TrimSpace(cfg.ColorProfile); profile != "" {
  101. profile = strings.ReplaceAll(strings.ReplaceAll(profile, "\r", ""), "\n", "")
  102. c.Writer.Header().Set("Color-Profile", profile)
  103. }
  104. if ping := strings.TrimSpace(cfg.PingType); ping != "" {
  105. if strings.EqualFold(ping, "http") {
  106. ping = "proxy"
  107. }
  108. c.Writer.Header().Set("Ping-Type", ping)
  109. }
  110. if cfg.AutoConnect {
  111. c.Writer.Header().Set("Subscription-Autoconnect", "1")
  112. autoType := strings.TrimSpace(cfg.AutoConnectType)
  113. switch strings.ToLower(autoType) {
  114. case "fastest":
  115. autoType = "lowestdelay"
  116. case "last":
  117. autoType = "lastused"
  118. }
  119. if autoType != "" {
  120. c.Writer.Header().Set("Subscription-Autoconnect-Type", autoType)
  121. }
  122. }
  123. if mode := strings.TrimSpace(cfg.PerAppMode); mode != "" && mode != "off" {
  124. switch strings.ToLower(mode) {
  125. case "include":
  126. mode = "on"
  127. case "exclude":
  128. mode = "bypass"
  129. }
  130. c.Writer.Header().Set("Per-App-Proxy-Mode", mode)
  131. if list := strings.TrimSpace(cfg.PerAppList); list != "" {
  132. c.Writer.Header().Set("Per-App-Proxy-List", list)
  133. }
  134. }
  135. }