visitor.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package server
  2. import (
  3. "errors"
  4. "golang.org/x/time/rate"
  5. "heckel.io/ntfy/util"
  6. "sync"
  7. "time"
  8. )
  9. const (
  10. // visitorExpungeAfter defines how long a visitor is active before it is removed from memory. This number
  11. // has to be very high to prevent e-mail abuse, but it doesn't really affect the other limits anyway, since
  12. // they are replenished faster (typically).
  13. visitorExpungeAfter = 24 * time.Hour
  14. )
  15. var (
  16. errVisitorLimitReached = errors.New("limit reached")
  17. )
  18. // visitor represents an API user, and its associated rate.Limiter used for rate limiting
  19. type visitor struct {
  20. config *Config
  21. messageCache *messageCache
  22. ip string
  23. requests *rate.Limiter
  24. emails *rate.Limiter
  25. subscriptions util.Limiter
  26. bandwidth util.Limiter
  27. firebase time.Time // Next allowed Firebase message
  28. seen time.Time
  29. mu sync.Mutex
  30. }
  31. type visitorStats struct {
  32. AttachmentFileSizeLimit int64 `json:"attachmentFileSizeLimit"`
  33. VisitorAttachmentBytesTotal int64 `json:"visitorAttachmentBytesTotal"`
  34. VisitorAttachmentBytesUsed int64 `json:"visitorAttachmentBytesUsed"`
  35. VisitorAttachmentBytesRemaining int64 `json:"visitorAttachmentBytesRemaining"`
  36. }
  37. func newVisitor(conf *Config, messageCache *messageCache, ip string) *visitor {
  38. return &visitor{
  39. config: conf,
  40. messageCache: messageCache,
  41. ip: ip,
  42. requests: rate.NewLimiter(rate.Every(conf.VisitorRequestLimitReplenish), conf.VisitorRequestLimitBurst),
  43. emails: rate.NewLimiter(rate.Every(conf.VisitorEmailLimitReplenish), conf.VisitorEmailLimitBurst),
  44. subscriptions: util.NewFixedLimiter(int64(conf.VisitorSubscriptionLimit)),
  45. bandwidth: util.NewBytesLimiter(conf.VisitorAttachmentDailyBandwidthLimit, 24*time.Hour),
  46. firebase: time.Unix(0, 0),
  47. seen: time.Now(),
  48. }
  49. }
  50. func (v *visitor) RequestAllowed() error {
  51. if !v.requests.Allow() {
  52. return errVisitorLimitReached
  53. }
  54. return nil
  55. }
  56. func (v *visitor) FirebaseAllowed() error {
  57. v.mu.Lock()
  58. defer v.mu.Unlock()
  59. if time.Now().Before(v.firebase) {
  60. return errVisitorLimitReached
  61. }
  62. return nil
  63. }
  64. func (v *visitor) FirebaseTemporarilyDeny() {
  65. v.mu.Lock()
  66. defer v.mu.Unlock()
  67. v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration)
  68. }
  69. func (v *visitor) EmailAllowed() error {
  70. if !v.emails.Allow() {
  71. return errVisitorLimitReached
  72. }
  73. return nil
  74. }
  75. func (v *visitor) SubscriptionAllowed() error {
  76. v.mu.Lock()
  77. defer v.mu.Unlock()
  78. if err := v.subscriptions.Allow(1); err != nil {
  79. return errVisitorLimitReached
  80. }
  81. return nil
  82. }
  83. func (v *visitor) RemoveSubscription() {
  84. v.mu.Lock()
  85. defer v.mu.Unlock()
  86. v.subscriptions.Allow(-1)
  87. }
  88. func (v *visitor) Keepalive() {
  89. v.mu.Lock()
  90. defer v.mu.Unlock()
  91. v.seen = time.Now()
  92. }
  93. func (v *visitor) BandwidthLimiter() util.Limiter {
  94. return v.bandwidth
  95. }
  96. func (v *visitor) Stale() bool {
  97. v.mu.Lock()
  98. defer v.mu.Unlock()
  99. return time.Since(v.seen) > visitorExpungeAfter
  100. }
  101. func (v *visitor) Stats() (*visitorStats, error) {
  102. attachmentsBytesUsed, err := v.messageCache.AttachmentBytesUsed(v.ip)
  103. if err != nil {
  104. return nil, err
  105. }
  106. attachmentsBytesRemaining := v.config.VisitorAttachmentTotalSizeLimit - attachmentsBytesUsed
  107. if attachmentsBytesRemaining < 0 {
  108. attachmentsBytesRemaining = 0
  109. }
  110. return &visitorStats{
  111. AttachmentFileSizeLimit: v.config.AttachmentFileSizeLimit,
  112. VisitorAttachmentBytesTotal: v.config.VisitorAttachmentTotalSizeLimit,
  113. VisitorAttachmentBytesUsed: attachmentsBytesUsed,
  114. VisitorAttachmentBytesRemaining: attachmentsBytesRemaining,
  115. }, nil
  116. }