Class: Rack::Throttle::Limiter
- Inherits:
-
Object
- Object
- Rack::Throttle::Limiter
- Defined in:
- lib/rack/throttle/limiter.rb
Overview
This is the base class for rate limiter implementations.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#app ⇒ Object
readonly
Returns the value of attribute app.
-
#options ⇒ Object
readonly
Returns the value of attribute options.
Instance Method Summary collapse
-
#allowed?(request) ⇒ Boolean
Returns
false
if the rate limit has been exceeded for the givenrequest
, ortrue
otherwise. -
#blacklisted?(request) ⇒ Boolean
abstract
Returns
true
if the originator of the givenrequest
is blacklisted (not honoring rate limits, and thus permanently forbidden access without the need to maintain further rate limit counters). - #cache ⇒ Hash protected
- #cache_get(key, default = nil) ⇒ Object protected
- #cache_has?(key) ⇒ Boolean protected
- #cache_key(request) ⇒ String protected
- #cache_set(key, value) protected
- #call(env) ⇒ Array(Integer, Hash, #each)
-
#call_on_reject(env) ⇒ Object
protected
Calls whatever object is passed with options[:on_reject] on initialize.
- #client_identifier(request) ⇒ String protected
-
#http_error(code, message = nil, headers = {}) ⇒ Array(Integer, Hash, #each)
protected
Outputs an HTTP
4xx
or5xx
response. -
#http_status(code) ⇒ String
protected
Returns the standard HTTP status message for the given status
code
. -
#initialize(app, options = {}) ⇒ Limiter
constructor
A new instance of Limiter.
-
#rate_limit_exceeded ⇒ Array(Integer, Hash, #each)
protected
Outputs a
Rate Limit Exceeded
error. - #request_start_time(request) ⇒ Float protected
-
#whitelisted?(request) ⇒ Boolean
abstract
Returns
true
if the originator of the givenrequest
is whitelisted (not subject to further rate limits).
Constructor Details
#initialize(app, options = {}) ⇒ Limiter
Returns a new instance of Limiter.
25 26 27 |
# File 'lib/rack/throttle/limiter.rb', line 25 def initialize(app, = {}) @app, @options = app, end |
Instance Attribute Details
#app ⇒ Object (readonly)
Returns the value of attribute app.
13 14 15 |
# File 'lib/rack/throttle/limiter.rb', line 13 def app @app end |
#options ⇒ Object (readonly)
Returns the value of attribute options.
14 15 16 |
# File 'lib/rack/throttle/limiter.rb', line 14 def @options end |
Instance Method Details
#allowed?(request) ⇒ Boolean
Returns false
if the rate limit has been exceeded for the given
request
, or true
otherwise.
Override this method in subclasses that implement custom rate limiter strategies.
52 53 54 55 56 57 58 |
# File 'lib/rack/throttle/limiter.rb', line 52 def allowed?(request) case when whitelisted?(request) then true when blacklisted?(request) then false else true # override in subclasses end end |
#blacklisted?(request) ⇒ Boolean
Returns true
if the originator of the given request
is blacklisted
(not honoring rate limits, and thus permanently forbidden access
without the need to maintain further rate limit counters).
The default implementation always returns false
. Override this
method in a subclass to implement custom blacklisting logic.
85 86 87 |
# File 'lib/rack/throttle/limiter.rb', line 85 def blacklisted?(request) false end |
#cache ⇒ Hash (protected)
98 99 100 101 102 103 |
# File 'lib/rack/throttle/limiter.rb', line 98 def cache case cache = ([:cache] ||= {}) when Proc then cache.call else cache end end |
#cache_get(key, default = nil) ⇒ Object (protected)
120 121 122 123 124 125 126 127 |
# File 'lib/rack/throttle/limiter.rb', line 120 def cache_get(key, default = nil) case when cache.respond_to?(:[]) cache[key] || default when cache.respond_to?(:get) cache.get(key) || default end end |
#cache_has?(key) ⇒ Boolean (protected)
107 108 109 110 111 112 113 114 115 |
# File 'lib/rack/throttle/limiter.rb', line 107 def cache_has?(key) case when cache.respond_to?(:has_key?) cache.has_key?(key) when cache.respond_to?(:get) cache.get(key) rescue false else false end end |
#cache_key(request) ⇒ String (protected)
154 155 156 157 158 159 160 161 162 163 |
# File 'lib/rack/throttle/limiter.rb', line 154 def cache_key(request) id = client_identifier(request) case when .has_key?(:key) [:key].call(request) when .has_key?(:key_prefix) [[:key_prefix], id].join(':') else id end end |
#cache_set(key, value) (protected)
This method returns an undefined value.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/rack/throttle/limiter.rb', line 133 def cache_set(key, value) case when cache.respond_to?(:[]=) begin cache[key] = value rescue TypeError => e # GDBM throws a "TypeError: can't convert Float into String" # exception when trying to store a Float. On the other hand, we # don't want to unnecessarily coerce the value to a String for # any stores that do support other data types (e.g. in-memory # hash objects). So, this is a compromise. cache[key] = value.to_s end when cache.respond_to?(:set) cache.set(key, value) end end |
#call(env) ⇒ Array(Integer, Hash, #each)
33 34 35 36 37 38 39 40 41 |
# File 'lib/rack/throttle/limiter.rb', line 33 def call(env) request = Rack::Request.new(env) if allowed?(request) app.call(env) else call_on_reject(env) rate_limit_exceeded end end |
#call_on_reject(env) ⇒ Object (protected)
Calls whatever object is passed with options[:on_reject] on initialize
92 93 94 |
# File 'lib/rack/throttle/limiter.rb', line 92 def call_on_reject(env) @options[:on_reject].call(env) if @options[:on_reject] end |
#client_identifier(request) ⇒ String (protected)
168 169 170 |
# File 'lib/rack/throttle/limiter.rb', line 168 def client_identifier(request) request.ip.to_s end |
#http_error(code, message = nil, headers = {}) ⇒ Array(Integer, Hash, #each) (protected)
Outputs an HTTP 4xx
or 5xx
response.
200 201 202 203 |
# File 'lib/rack/throttle/limiter.rb', line 200 def http_error(code, = nil, headers = {}) [code, {'Content-Type' => 'text/plain; charset=utf-8'}.merge(headers), http_status(code) + (.nil? ? "\n" : " (#{})\n")] end |
#http_status(code) ⇒ String (protected)
Returns the standard HTTP status message for the given status code
.
210 211 212 |
# File 'lib/rack/throttle/limiter.rb', line 210 def http_status(code) [code, Rack::Utils::HTTP_STATUS_CODES[code]].join(' ') end |
#rate_limit_exceeded ⇒ Array(Integer, Hash, #each) (protected)
Outputs a Rate Limit Exceeded
error.
188 189 190 191 |
# File 'lib/rack/throttle/limiter.rb', line 188 def rate_limit_exceeded headers = respond_to?(:retry_after) ? {'Retry-After' => retry_after.to_f.ceil.to_s} : {} http_error([:code] || 403, [:message] || 'Rate Limit Exceeded', headers) end |
#request_start_time(request) ⇒ Float (protected)
175 176 177 178 179 180 181 182 |
# File 'lib/rack/throttle/limiter.rb', line 175 def request_start_time(request) case when request.env.has_key?('HTTP_X_REQUEST_START') request.env['HTTP_X_REQUEST_START'].to_f / 1000 else Time.now.to_f end end |
#whitelisted?(request) ⇒ Boolean
Returns true
if the originator of the given request
is whitelisted
(not subject to further rate limits).
The default implementation always returns false
. Override this
method in a subclass to implement custom whitelisting logic.
70 71 72 |
# File 'lib/rack/throttle/limiter.rb', line 70 def whitelisted?(request) false end |