mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-04-27 15:07:41 +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
|
```ruby
|
||||||
Rack::Attack.throttled_response = lambda do |env|
|
Rack::Attack.throttled_response = lambda do |env|
|
||||||
now = Time.now
|
|
||||||
match_data = env['rack.attack.match_data']
|
match_data = env['rack.attack.match_data']
|
||||||
|
now = match_data[:epoch_time]
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'X-RateLimit-Limit' => match_data[:limit].to_s,
|
'X-RateLimit-Limit' => match_data[:limit].to_s,
|
||||||
'X-RateLimit-Remaining' => '0',
|
'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"]]
|
[ 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:
|
For responses that did not exceed a throttle limit, Rack::Attack annotates the env with match data:
|
||||||
|
|
||||||
```ruby
|
```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
|
## Logging & Instrumentation
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ module Rack
|
||||||
class Attack
|
class Attack
|
||||||
class Cache
|
class Cache
|
||||||
attr_accessor :prefix
|
attr_accessor :prefix
|
||||||
|
attr_reader :last_epoch_time
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
self.store = ::Rails.cache if defined?(::Rails.cache)
|
self.store = ::Rails.cache if defined?(::Rails.cache)
|
||||||
|
|
@ -43,10 +44,10 @@ module Rack
|
||||||
private
|
private
|
||||||
|
|
||||||
def key_and_expiry(unprefixed_key, period)
|
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
|
# Add 1 to expires_in to avoid timing error: https://git.io/i1PHXA
|
||||||
expires_in = (period - (epoch_time % period) + 1).to_i
|
expires_in = (period - (@last_epoch_time % period) + 1).to_i
|
||||||
["#{prefix}:#{(epoch_time / period).to_i}:#{unprefixed_key}", expires_in]
|
["#{prefix}:#{(@last_epoch_time / period).to_i}:#{unprefixed_key}", expires_in]
|
||||||
end
|
end
|
||||||
|
|
||||||
def do_count(key, expires_in)
|
def do_count(key, expires_in)
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,15 @@ module Rack
|
||||||
current_limit = limit.respond_to?(:call) ? limit.call(request) : limit
|
current_limit = limit.respond_to?(:call) ? limit.call(request) : limit
|
||||||
key = "#{name}:#{discriminator}"
|
key = "#{name}:#{discriminator}"
|
||||||
count = cache.count(key, current_period)
|
count = cache.count(key, current_period)
|
||||||
|
epoch_time = cache.last_epoch_time
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
:count => count,
|
:count => count,
|
||||||
:period => current_period,
|
:period => current_period,
|
||||||
:limit => current_limit
|
:limit => current_limit,
|
||||||
|
:epoch_time => epoch_time
|
||||||
}
|
}
|
||||||
|
|
||||||
(request.env['rack.attack.throttle_data'] ||= {})[name] = data
|
(request.env['rack.attack.throttle_data'] ||= {})[name] = data
|
||||||
|
|
||||||
(count > current_limit).tap do |throttled|
|
(count > current_limit).tap do |throttled|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ describe 'Rack::Attack.throttle' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should populate throttle data' do
|
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
|
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -39,7 +39,7 @@ describe 'Rack::Attack.throttle' do
|
||||||
it 'should tag the env' do
|
it 'should tag the env' do
|
||||||
last_request.env['rack.attack.matched'].must_equal 'ip/sec'
|
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_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')
|
last_request.env['rack.attack.match_discriminator'].must_equal('1.2.3.4')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ describe 'Rack::Attack.throttle with limit as proc' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should populate throttle data' do
|
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
|
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -91,7 +91,7 @@ describe 'Rack::Attack.throttle with period as proc' do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should populate throttle data' do
|
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
|
last_request.env['rack.attack.throttle_data']['ip/sec'].must_equal data
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue