Merge remote-tracking branch 'doliveirakn/master'

This commit is contained in:
Gonzalo Rodriguez 2018-06-29 11:17:19 -03:00
commit 73bc739d5a
No known key found for this signature in database
GPG key ID: 5DB8B81B049B8AB1
4 changed files with 15 additions and 11 deletions

View file

@ -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

View file

@ -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)

View file

@ -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|

View file

@ -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