Class: Whoosh::Auth::RateLimiter
- Inherits:
-
Object
- Object
- Whoosh::Auth::RateLimiter
- Defined in:
- lib/whoosh/auth/rate_limiter.rb
Instance Method Summary collapse
- #check!(key, path, tier: nil) ⇒ Object
-
#initialize(default_limit: 60, default_period: 60, on_store_failure: :fail_open) ⇒ RateLimiter
constructor
A new instance of RateLimiter.
- #remaining(key, path, tier: nil) ⇒ Object
- #rule(path, limit:, period:) ⇒ Object
- #tier(name, limit: nil, period: nil, unlimited: false) ⇒ Object
Constructor Details
#initialize(default_limit: 60, default_period: 60, on_store_failure: :fail_open) ⇒ RateLimiter
Returns a new instance of RateLimiter.
6 7 8 9 10 11 12 13 14 |
# File 'lib/whoosh/auth/rate_limiter.rb', line 6 def initialize(default_limit: 60, default_period: 60, on_store_failure: :fail_open) @default_limit = default_limit @default_period = default_period @on_store_failure = on_store_failure @rules = {} @tiers = {} @store = {} @mutex = Mutex.new end |
Instance Method Details
#check!(key, path, tier: nil) ⇒ Object
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/whoosh/auth/rate_limiter.rb', line 24 def check!(key, path, tier: nil) limits = resolve_limits(path, tier) return if limits[:unlimited] bucket_key = "#{key}:#{path}" @mutex.synchronize do record = @store[bucket_key] now = Time.now.to_f if record.nil? || (now - record[:window_start]) >= limits[:period] @store[bucket_key] = { count: 1, window_start: now } return end if record[:count] >= limits[:limit] retry_after = (limits[:period] - (now - record[:window_start])).ceil raise Errors::RateLimitExceeded.new(retry_after: retry_after) end record[:count] += 1 end rescue NoMethodError, TypeError if @on_store_failure == :fail_closed raise Errors::RateLimitExceeded.new("Rate limit store unavailable", retry_after: 60) end end |
#remaining(key, path, tier: nil) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/whoosh/auth/rate_limiter.rb', line 52 def remaining(key, path, tier: nil) limits = resolve_limits(path, tier) return Float::INFINITY if limits[:unlimited] bucket_key = "#{key}:#{path}" @mutex.synchronize do record = @store[bucket_key] return limits[:limit] unless record now = Time.now.to_f return limits[:limit] if (now - record[:window_start]) >= limits[:period] [limits[:limit] - record[:count], 0].max end end |
#rule(path, limit:, period:) ⇒ Object
16 17 18 |
# File 'lib/whoosh/auth/rate_limiter.rb', line 16 def rule(path, limit:, period:) @rules[path] = { limit: limit, period: period } end |
#tier(name, limit: nil, period: nil, unlimited: false) ⇒ Object
20 21 22 |
# File 'lib/whoosh/auth/rate_limiter.rb', line 20 def tier(name, limit: nil, period: nil, unlimited: false) @tiers[name] = { limit: limit, period: period, unlimited: unlimited } end |