rack-attack/lib/rack/attack/throttle.rb
Kyle d'Oliveira 9dbece5272 Add an reader for the epoch_time variable in the cache so that it can also be returned in the data from the throttle.
This is allows access to the same time that the cache uses for the count. This can be important for clients that want to provide rate limit information for well-behaved clients
2018-02-15 14:45:35 -08:00

52 lines
1.6 KiB
Ruby

module Rack
class Attack
class Throttle
MANDATORY_OPTIONS = [:limit, :period].freeze
attr_reader :name, :limit, :period, :block, :type
def initialize(name, options, block)
@name, @block = name, block
MANDATORY_OPTIONS.each do |opt|
raise ArgumentError.new("Must pass #{opt.inspect} option") unless options[opt]
end
@limit = options[:limit]
@period = options[:period].respond_to?(:call) ? options[:period] : options[:period].to_i
@type = options.fetch(:type, :throttle)
end
def cache
Rack::Attack.cache
end
def [](req)
discriminator = block[req]
return false unless discriminator
current_period = period.respond_to?(:call) ? period.call(req) : period
current_limit = limit.respond_to?(:call) ? limit.call(req) : limit
key = "#{name}:#{discriminator}"
count = cache.count(key, current_period)
epoch_time = cache.last_epoch_time
data = {
:count => count,
:period => current_period,
:limit => current_limit,
:epoch_time => epoch_time
}
(req.env['rack.attack.throttle_data'] ||= {})[name] = data
(count > current_limit).tap do |throttled|
if throttled
req.env['rack.attack.matched'] = name
req.env['rack.attack.match_discriminator'] = discriminator
req.env['rack.attack.match_type'] = type
req.env['rack.attack.match_data'] = data
Rack::Attack.instrument(req)
end
end
end
end
end
end