mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-03-25 09:25:49 +00:00
Merge remote-tracking branch 'doliveirakn/master'
This commit is contained in:
commit
73bc739d5a
4 changed files with 15 additions and 11 deletions
|
|
@ -304,13 +304,13 @@ Here's an example response that includes conventional `X-RateLimit-*` headers:
|
|||
|
||||
```ruby
|
||||
Rack::Attack.throttled_response = lambda do |env|
|
||||
now = Time.now
|
||||
match_data = env['rack.attack.match_data']
|
||||
now = match_data[:epoch_time]
|
||||
|
||||
headers = {
|
||||
'X-RateLimit-Limit' => match_data[:limit].to_s,
|
||||
'X-RateLimit-Remaining' => '0',
|
||||
'X-RateLimit-Reset' => (now + (match_data[:period] - now.to_i % match_data[:period])).to_s
|
||||
'X-RateLimit-Reset' => (now + (match_data[:period] - now % match_data[:period])).to_s
|
||||
}
|
||||
|
||||
[ 429, headers, ["Throttled\n"]]
|
||||
|
|
@ -321,7 +321,7 @@ end
|
|||
For responses that did not exceed a throttle limit, Rack::Attack annotates the env with match data:
|
||||
|
||||
```ruby
|
||||
request.env['rack.attack.throttle_data'][name] # => { :count => n, :period => p, :limit => l }
|
||||
request.env['rack.attack.throttle_data'][name] # => { :count => n, :period => p, :limit => l, :epoch_time => t }
|
||||
```
|
||||
|
||||
## Logging & Instrumentation
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ module Rack
|
|||
class Attack
|
||||
class Cache
|
||||
attr_accessor :prefix
|
||||
attr_reader :last_epoch_time
|
||||
|
||||
def initialize
|
||||
self.store = ::Rails.cache if defined?(::Rails.cache)
|
||||
|
|
@ -43,10 +44,10 @@ module Rack
|
|||
private
|
||||
|
||||
def key_and_expiry(unprefixed_key, period)
|
||||
epoch_time = Time.now.to_i
|
||||
@last_epoch_time = Time.now.to_i
|
||||
# Add 1 to expires_in to avoid timing error: https://git.io/i1PHXA
|
||||
expires_in = (period - (epoch_time % period) + 1).to_i
|
||||
["#{prefix}:#{(epoch_time / period).to_i}:#{unprefixed_key}", expires_in]
|
||||
expires_in = (period - (@last_epoch_time % period) + 1).to_i
|
||||
["#{prefix}:#{(@last_epoch_time / period).to_i}:#{unprefixed_key}", expires_in]
|
||||
end
|
||||
|
||||
def do_count(key, expires_in)
|
||||
|
|
|
|||
|
|
@ -28,12 +28,15 @@ module Rack
|
|||
current_limit = limit.respond_to?(:call) ? limit.call(request) : 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
|
||||
:limit => current_limit,
|
||||
:epoch_time => epoch_time
|
||||
}
|
||||
|
||||
(request.env['rack.attack.throttle_data'] ||= {})[name] = data
|
||||
|
||||
(count > current_limit).tap do |throttled|
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ describe 'Rack::Attack.throttle' do
|
|||
end
|
||||
|
||||
it 'should populate throttle data' do
|
||||
data = { :count => 1, :limit => 1, :period => @period }
|
||||
data = { :count => 1, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i }
|
||||
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
||||
end
|
||||
end
|
||||
|
|
@ -39,7 +39,7 @@ describe 'Rack::Attack.throttle' do
|
|||
it 'should tag the env' do
|
||||
last_request.env['rack.attack.matched'].must_equal 'ip/sec'
|
||||
last_request.env['rack.attack.match_type'].must_equal :throttle
|
||||
last_request.env['rack.attack.match_data'].must_equal(:count => 2, :limit => 1, :period => @period)
|
||||
last_request.env['rack.attack.match_data'].must_equal(:count => 2, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i)
|
||||
last_request.env['rack.attack.match_discriminator'].must_equal('1.2.3.4')
|
||||
end
|
||||
|
||||
|
|
@ -67,7 +67,7 @@ describe 'Rack::Attack.throttle with limit as proc' do
|
|||
end
|
||||
|
||||
it 'should populate throttle data' do
|
||||
data = { :count => 1, :limit => 1, :period => @period }
|
||||
data = { :count => 1, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i }
|
||||
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
||||
end
|
||||
end
|
||||
|
|
@ -91,7 +91,7 @@ describe 'Rack::Attack.throttle with period as proc' do
|
|||
end
|
||||
|
||||
it 'should populate throttle data' do
|
||||
data = { :count => 1, :limit => 1, :period => @period }
|
||||
data = { :count => 1, :limit => 1, :period => @period, epoch_time: Rack::Attack.cache.last_epoch_time.to_i }
|
||||
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue