Class: Rack::RateLimiter
- Inherits:
-
Object
- Object
- Rack::RateLimiter
- Defined in:
- lib/rack/rate_limiter.rb
Instance Method Summary collapse
- #call(env, options = {}) ⇒ Object
- #client_id(request) ⇒ Object
- #generate_key(request) ⇒ Object
-
#initialize(app, options = {}) ⇒ RateLimiter
constructor
A new instance of RateLimiter.
- #limit_exceeded! ⇒ Object
- #rate_limited?(request) ⇒ Boolean
- #redis ⇒ Object
Constructor Details
#initialize(app, options = {}) ⇒ RateLimiter
Returns a new instance of RateLimiter.
7 8 9 10 11 12 13 14 15 16 |
# File 'lib/rack/rate_limiter.rb', line 7 def initialize(app, ={}) @app = app @options ={ :redis => nil, :limit => nil, :max_requests => 60, :interval => 60, # in seconds :redis_namespace => 'rate_limiter' }.merge() end |
Instance Method Details
#call(env, options = {}) ⇒ Object
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/rack/rate_limiter.rb', line 18 def call(env, ={}) request = Rack::Request.new(env) status, headers, body = @app.call(env) return status, headers, body unless rate_limited?(request) begin key = generate_key(request) redis.setex(key, @options[:interval], @options[:max_requests]) unless redis.exists(key) @rate_remaining = redis.decr(key) return limit_exceeded! if @rate_remaining < 0 rescue ::Errno::ECONNREFUSED return [status, headers, body] end headers.merge!({'X-RateLimit-Limit' => @options[:max_requests].to_s, 'X-RateLimit-Remaining' => @rate_remaining.to_s }) return status, headers, body end |
#client_id(request) ⇒ Object
45 46 47 |
# File 'lib/rack/rate_limiter.rb', line 45 def client_id(request) request.ip.to_s end |
#generate_key(request) ⇒ Object
41 42 43 |
# File 'lib/rack/rate_limiter.rb', line 41 def generate_key(request) "#{client_id(request)}" end |
#limit_exceeded! ⇒ Object
59 60 61 62 63 64 65 66 |
# File 'lib/rack/rate_limiter.rb', line 59 def limit_exceeded! code = 403 body = {:error => {:code => 403, :message => "Rate Limit Exceeded"}}.to_json [code, {'Content-Type' => 'application/json; charset=utf-8', 'Content-Lenght' => body.size.to_s }, body] end |
#rate_limited?(request) ⇒ Boolean
49 50 51 52 53 54 55 56 57 |
# File 'lib/rack/rate_limiter.rb', line 49 def rate_limited?(request) limit = @options[:limit] unless limit[:domain].nil? return request.host == limit[:domain] end return false end |
#redis ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/rack/rate_limiter.rb', line 68 def redis if @options[:redis].nil? begin @redis = Redis::Namespace.new(@options[:redis_namespace], :redis => Redis.new(:thread_safe => true)) rescue Errno::ECONNREFUSED end else @redis = @options[:redis] end @redis end |