Class: Rack::ApiKeyLimit::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/rack_api_key_limit/base.rb

Direct Known Subclasses

Hourly

Instance Method Summary collapse

Constructor Details

#initialize(app, options) ⇒ Base

Returns a new instance of Base.



4
5
6
7
# File 'lib/rack_api_key_limit/base.rb', line 4

def initialize(app, options)
  @app = app
  @options = options
end

Instance Method Details

#allowed?(request, key) ⇒ Boolean

Returns:

  • (Boolean)


74
75
76
77
# File 'lib/rack_api_key_limit/base.rb', line 74

def allowed?(request, key)
  return true unless has_param?(request) # always allowed if no key present
  cache.increment(key, limit_seconds) and return true if remaining(key) > 0
end

#cacheObject



13
14
15
# File 'lib/rack_api_key_limit/base.rb', line 13

def cache
  @options[:cache]
end

#call(env) ⇒ Object



17
18
19
20
21
# File 'lib/rack_api_key_limit/base.rb', line 17

def call(env)
  request = Rack::Request.new(env)
  key = get_key(request, cache)
  allowed?(request, key) ? not_rate_limited(env, request, key) : rate_limit_exceeded(key)
end

#get_key(request, cache) ⇒ Object

Raises:

  • (NotImplementedError)


35
36
37
# File 'lib/rack_api_key_limit/base.rb', line 35

def get_key(request, cache)
  raise NotImplementedError.new("You must implement get_key.")
end

#has_param?(request) ⇒ Boolean

Returns:

  • (Boolean)


31
32
33
# File 'lib/rack_api_key_limit/base.rb', line 31

def has_param?(request)
  request.params.has_key?(param_name)
end

#http_error(code, message = nil, headers = {}) ⇒ Object



79
80
81
82
83
# File 'lib/rack_api_key_limit/base.rb', line 79

def http_error(code, message = nil, headers = {})
  [code, {'Content-Type' => 'text/plain; charset=utf-8'}.merge(headers),
    [http_status(code) + (message.nil? ? "\n" : " (#{message})\n")]
  ]
end

#http_status(code) ⇒ Object



85
86
87
# File 'lib/rack_api_key_limit/base.rb', line 85

def http_status(code)
  [code, Rack::Utils::HTTP_STATUS_CODES[code]].join(' ')
end

#limit_secondsObject

Raises:

  • (NotImplementedError)


49
50
51
# File 'lib/rack_api_key_limit/base.rb', line 49

def limit_seconds
  raise NotImplementedError.new("You must implement limit_seconds.")
end

#not_rate_limited(env, request, key) ⇒ Object



39
40
41
42
43
# File 'lib/rack_api_key_limit/base.rb', line 39

def not_rate_limited(env, request, key)
  status, headers, response = @app.call(env)
  headers = headers.merge(rate_limit_headers(key)) if has_param?(request)
  [status, headers, response]
end

#optionsObject



9
10
11
# File 'lib/rack_api_key_limit/base.rb', line 9

def options
  @options || {}
end

#param(request) ⇒ Object



27
28
29
# File 'lib/rack_api_key_limit/base.rb', line 27

def param(request)
  request.params[param_name]
end

#param_nameObject



23
24
25
# File 'lib/rack_api_key_limit/base.rb', line 23

def param_name
  options[:param_name] || "api_key"
end

#rate_limit_exceeded(key) ⇒ Object



61
62
63
# File 'lib/rack_api_key_limit/base.rb', line 61

def rate_limit_exceeded(key)
  http_error(options[:status] || 429, options[:message] || 'Rate Limit Exceeded', rate_limit_headers(key))
end

#rate_limit_headers(key) ⇒ Object



53
54
55
56
57
58
59
# File 'lib/rack_api_key_limit/base.rb', line 53

def rate_limit_headers(key)
  headers = {}
  headers["X-RateLimit-Limit"] = request_limit.to_s
  headers["X-RateLimit-Remaining"] = remaining(key).to_s
  headers["X-RateLimit-Reset"] = retry_after.to_f.ceil.to_s if respond_to?(:retry_after)
  headers
end

#remaining(key) ⇒ Object



70
71
72
# File 'lib/rack_api_key_limit/base.rb', line 70

def remaining(key)
  request_limit - request_count(key)
end

#request_count(key) ⇒ Object



65
66
67
68
# File 'lib/rack_api_key_limit/base.rb', line 65

def request_count(key)
  request_count = cache.get(key)
  (request_count.to_i if request_count) || 0
end

#request_limitObject



45
46
47
# File 'lib/rack_api_key_limit/base.rb', line 45

def request_limit
  options[:request_limit] || 150
end