grape-attack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +137 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/grape-attack.gemspec +30 -0
- data/lib/grape/attack/adapters/memory.rb +31 -0
- data/lib/grape/attack/adapters/redis.rb +61 -0
- data/lib/grape/attack/configurable.rb +17 -0
- data/lib/grape/attack/configuration.rb +14 -0
- data/lib/grape/attack/counter.rb +40 -0
- data/lib/grape/attack/exceptions.rb +7 -0
- data/lib/grape/attack/extension.rb +14 -0
- data/lib/grape/attack/limiter.rb +58 -0
- data/lib/grape/attack/options.rb +19 -0
- data/lib/grape/attack/request.rb +37 -0
- data/lib/grape/attack/throttle.rb +28 -0
- data/lib/grape/attack/version.rb +5 -0
- data/lib/grape/attack.rb +14 -0
- metadata +165 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ff307dec592ab729847359d3da1bbdfe624136b5
|
4
|
+
data.tar.gz: f20eff73c56b429c680a71a2db4a0cd7d53fd6f6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8639ff18ef9e6d2a6123079bcd9071030e2bb5ee5d22056207ed48c01c4aa16667d4cde4ac74e1e175acf07ef69a2779b979d9afb3b2be4ace12f5af46c0b82a
|
7
|
+
data.tar.gz: b8849164cda638b09ea9e41fcad405778411096b6ca06876bee7983be090332f00b2710134b077d81a311230c5bf47bc52f64a2537d1e428c3c32fe0ecb3d3a2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Pierre-Louis Gottfrois
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
[](https://codeclimate.com/github/gottfrois/grape-attack)
|
2
|
+
|
3
|
+
# Grape::Attack
|
4
|
+
|
5
|
+
A middleware for Grape to add endpoint-specific throttling.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'grape-attack'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install grape-attack
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Mount the middleware in your API:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
class MyApi < Grape::API
|
29
|
+
use Grape::Attack::Throttle
|
30
|
+
end
|
31
|
+
```
|
32
|
+
|
33
|
+
Define limits per endpoints using `throttle` DSL:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
class MyApi < Grape::API
|
37
|
+
|
38
|
+
use Grape::Attack::Throttle
|
39
|
+
|
40
|
+
resources :comments do
|
41
|
+
|
42
|
+
throttle max: 10, per: 1.minute
|
43
|
+
get do
|
44
|
+
Comment.all
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
Use any [ActiveSupport Time extension to Numeric](http://edgeguides.rubyonrails.org/active_support_core_extensions.html#time) object.
|
52
|
+
|
53
|
+
By default it will use the request ip address to identity the client making the request.
|
54
|
+
You can pass your own identifier using a `Proc`:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
class MyApi < Grape::API
|
58
|
+
|
59
|
+
use Grape::Attack::Throttle
|
60
|
+
|
61
|
+
helpers do
|
62
|
+
def current_user
|
63
|
+
@current_user ||= User.authorize!(env)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
resources :comments do
|
68
|
+
|
69
|
+
throttle max: 100, per: 1.day, identifier: Proc.new { current_user.id }
|
70
|
+
get do
|
71
|
+
Comment.all
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
When rate limit is reached, it will raise `Grape::Attack::RateLimitExceededError` exception.
|
79
|
+
You can catch the exception using `rescue_from`:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
class MyApi < Grape::API
|
83
|
+
|
84
|
+
use Grape::Attack::Throttle
|
85
|
+
|
86
|
+
rescue_from Grape::Attack::RateLimitExceededError do |e|
|
87
|
+
error!({ message: e.message }, 403)
|
88
|
+
end
|
89
|
+
|
90
|
+
resources :comments do
|
91
|
+
|
92
|
+
throttle max: 100, per: 1.day
|
93
|
+
get do
|
94
|
+
Comment.all
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
Which would result in the following http response:
|
102
|
+
|
103
|
+
```
|
104
|
+
HTTP/1.1 403 Forbidden
|
105
|
+
Content-Type: application/json
|
106
|
+
|
107
|
+
{"message":"API rate limit exceeded for xxx.xxx.xxx.xxx."}
|
108
|
+
```
|
109
|
+
|
110
|
+
Finally the following headers will automatically be set:
|
111
|
+
|
112
|
+
* `X-RateLimit-Limit` -- The maximum number of requests that the consumer is permitted to make per specified period.
|
113
|
+
* `X-RateLimit-Remaining` -- The number of requests remaining in the current rate limit window.
|
114
|
+
* `X-RateLimit-Reset` -- The time at which the current rate limit window resets in [UTC epoch seconds](https://en.wikipedia.org/wiki/Unix_time).
|
115
|
+
|
116
|
+
## Adapters
|
117
|
+
|
118
|
+
Adapters are used to store the rate counter.
|
119
|
+
Currently there is only a Redis adapter. You can set redis client url through `env['REDIS_URL']` varialble.
|
120
|
+
|
121
|
+
Defaults to `redis://localhost:6379/0`.
|
122
|
+
|
123
|
+
## Development
|
124
|
+
|
125
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
126
|
+
|
127
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
128
|
+
|
129
|
+
## Contributing
|
130
|
+
|
131
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/grape-attack.
|
132
|
+
|
133
|
+
|
134
|
+
## License
|
135
|
+
|
136
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
137
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "grape/attack"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'grape/attack/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "grape-attack"
|
8
|
+
spec.version = Grape::Attack::VERSION
|
9
|
+
spec.authors = ["Pierre-Louis Gottfrois"]
|
10
|
+
spec.email = ["pierrelouis.gottfrois@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A middleware for Grape to add endpoint-specific throttling.}
|
13
|
+
spec.description = %q{A middleware for Grape to add endpoint-specific throttling.}
|
14
|
+
spec.homepage = ""
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.require_paths = ["lib"]
|
21
|
+
|
22
|
+
spec.add_dependency "grape", ">= 0.10"
|
23
|
+
spec.add_dependency "redis-namespace"
|
24
|
+
spec.add_dependency "activemodel", ">= 4.0"
|
25
|
+
spec.add_dependency "activesupport", ">= 4.0"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "rspec"
|
30
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Grape
|
2
|
+
module Attack
|
3
|
+
module Adapters
|
4
|
+
class Memory
|
5
|
+
|
6
|
+
attr_reader :data
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@data = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def get(key)
|
13
|
+
data[key]
|
14
|
+
end
|
15
|
+
|
16
|
+
def incr(key)
|
17
|
+
data[key] ||= 0
|
18
|
+
data[key] += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def expire(key, ttl_in_seconds)
|
22
|
+
end
|
23
|
+
|
24
|
+
def atomically(&block)
|
25
|
+
block.call
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'redis-namespace'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Attack
|
5
|
+
module Adapters
|
6
|
+
class Redis
|
7
|
+
|
8
|
+
attr_reader :broker
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@broker = ::Redis::Namespace.new("grape-attack:#{env}:thottle", redis: ::Redis.new(url: url))
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(key)
|
15
|
+
with_custom_exception do
|
16
|
+
broker.get(key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def incr(key)
|
21
|
+
with_custom_exception do
|
22
|
+
broker.incr(key)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def expire(key, ttl_in_seconds)
|
27
|
+
with_custom_exception do
|
28
|
+
broker.expire(key, ttl_in_seconds)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def atomically(&block)
|
33
|
+
broker.multi(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def with_custom_exception(&block)
|
39
|
+
block.call
|
40
|
+
rescue ::Redis::BaseError => e
|
41
|
+
raise ::Grape::Attack::StoreError.new(e.message)
|
42
|
+
end
|
43
|
+
|
44
|
+
def env
|
45
|
+
if defined?(::Rails)
|
46
|
+
::Rails.env
|
47
|
+
elsif defined?(RACK_ENV)
|
48
|
+
RACK_ENV
|
49
|
+
else
|
50
|
+
ENV['RACK_ENV']
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def url
|
55
|
+
ENV['REDIS_URL'] || 'redis://localhost:6379/0'
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Grape
|
2
|
+
module Attack
|
3
|
+
class Counter
|
4
|
+
|
5
|
+
attr_reader :request, :adapter
|
6
|
+
|
7
|
+
def initialize(request, adapter)
|
8
|
+
@request = request
|
9
|
+
@adapter = adapter
|
10
|
+
end
|
11
|
+
|
12
|
+
def value
|
13
|
+
@value ||= begin
|
14
|
+
adapter.get(key).to_i
|
15
|
+
rescue ::Grape::Attack::StoreError
|
16
|
+
1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def update
|
21
|
+
adapter.atomically do
|
22
|
+
adapter.incr(key)
|
23
|
+
adapter.expire(key, ttl_in_seconds)
|
24
|
+
end
|
25
|
+
rescue ::Grape::Attack::StoreError
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def key
|
31
|
+
"#{request.method}:#{request.path}:#{request.client_identifier}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def ttl_in_seconds
|
35
|
+
request.throttle_options.per.to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'grape/attack/request'
|
2
|
+
require 'grape/attack/counter'
|
3
|
+
|
4
|
+
module Grape
|
5
|
+
module Attack
|
6
|
+
class Limiter
|
7
|
+
|
8
|
+
attr_reader :request, :adapter, :counter
|
9
|
+
|
10
|
+
def initialize(env, adapter = ::Grape::Attack.config.adapter)
|
11
|
+
@request = ::Grape::Attack::Request.new(env)
|
12
|
+
@adapter = adapter
|
13
|
+
@counter = ::Grape::Attack::Counter.new(@request, @adapter)
|
14
|
+
end
|
15
|
+
|
16
|
+
def call!
|
17
|
+
return if disable?
|
18
|
+
return unless throttle?
|
19
|
+
|
20
|
+
if allowed?
|
21
|
+
update_counter
|
22
|
+
set_rate_limit_headers
|
23
|
+
else
|
24
|
+
fail ::Grape::Attack::RateLimitExceededError.new("API rate limit exceeded for #{request.client_identifier}.")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def disable?
|
31
|
+
::Grape::Attack.config.disable.call
|
32
|
+
end
|
33
|
+
|
34
|
+
def throttle?
|
35
|
+
request.throttle?
|
36
|
+
end
|
37
|
+
|
38
|
+
def allowed?
|
39
|
+
counter.value < max_requests_allowed
|
40
|
+
end
|
41
|
+
|
42
|
+
def update_counter
|
43
|
+
counter.update
|
44
|
+
end
|
45
|
+
|
46
|
+
# Fix when https://github.com/ruby-grape/grape/issues/1069
|
47
|
+
# For now we use route_setting to store :remaining value.
|
48
|
+
def set_rate_limit_headers
|
49
|
+
request.context.route_setting(:throttle)[:remaining] = [0, max_requests_allowed - (counter.value + 1)].max
|
50
|
+
end
|
51
|
+
|
52
|
+
def max_requests_allowed
|
53
|
+
request.throttle_options.max.to_i
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'active_model'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Attack
|
5
|
+
class Options
|
6
|
+
include ActiveModel::Model
|
7
|
+
|
8
|
+
attr_accessor :max, :per, :identifier, :remaining
|
9
|
+
|
10
|
+
validates :max, presence: true
|
11
|
+
validates :per, presence: true
|
12
|
+
|
13
|
+
def identifier
|
14
|
+
@identifier || Proc.new {}
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'grape/attack/options'
|
2
|
+
|
3
|
+
module Grape
|
4
|
+
module Attack
|
5
|
+
class Request
|
6
|
+
|
7
|
+
attr_reader :env, :context, :request, :throttle_options
|
8
|
+
|
9
|
+
def initialize(env)
|
10
|
+
@env = env
|
11
|
+
@context = env['api.endpoint']
|
12
|
+
@request = @context.routes.first
|
13
|
+
@throttle_options = ::Grape::Attack::Options.new(@context.route_setting(:throttle))
|
14
|
+
end
|
15
|
+
|
16
|
+
def method
|
17
|
+
request.route_method
|
18
|
+
end
|
19
|
+
|
20
|
+
def path
|
21
|
+
request.route_path
|
22
|
+
end
|
23
|
+
|
24
|
+
def client_identifier
|
25
|
+
context.instance_eval(&throttle_options.identifier) || env['REMOTE_ADDR']
|
26
|
+
end
|
27
|
+
|
28
|
+
def throttle?
|
29
|
+
return false unless context.route_setting(:throttle).present?
|
30
|
+
return true if throttle_options.valid?
|
31
|
+
|
32
|
+
fail ArgumentError.new(throttle_options.errors.full_messages)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'grape'
|
2
|
+
require 'grape/attack/limiter'
|
3
|
+
|
4
|
+
module Grape
|
5
|
+
module Attack
|
6
|
+
class Throttle < Grape::Middleware::Base
|
7
|
+
|
8
|
+
def before
|
9
|
+
::Grape::Attack::Limiter.new(env).call!
|
10
|
+
end
|
11
|
+
|
12
|
+
# Fix when https://github.com/ruby-grape/grape/issues/1069
|
13
|
+
# For now we use route_setting to store :remaining value.
|
14
|
+
def after
|
15
|
+
request = ::Grape::Attack::Request.new(env)
|
16
|
+
|
17
|
+
return if ::Grape::Attack.config.disable.call
|
18
|
+
return unless request.throttle?
|
19
|
+
|
20
|
+
@app_response['X-RateLimit-Limit'] = request.context.route_setting(:throttle)[:max].to_s
|
21
|
+
@app_response['X-RateLimit-Remaining'] = request.context.route_setting(:throttle)[:remaining].to_s
|
22
|
+
@app_response['X-RateLimit-Reset'] = request.context.route_setting(:throttle)[:per].from_now.to_i.to_s
|
23
|
+
@app_response
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/grape/attack.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'active_support/core_ext/numeric/time.rb'
|
2
|
+
require 'grape/attack/version'
|
3
|
+
require 'grape/attack/adapters/redis'
|
4
|
+
require 'grape/attack/adapters/memory'
|
5
|
+
require 'grape/attack/configurable'
|
6
|
+
require 'grape/attack/extension'
|
7
|
+
require 'grape/attack/exceptions'
|
8
|
+
require 'grape/attack/throttle'
|
9
|
+
|
10
|
+
module Grape
|
11
|
+
module Attack
|
12
|
+
extend Configurable
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grape-attack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pierre-Louis Gottfrois
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: grape
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.10'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redis-namespace
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: activemodel
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: bundler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.10'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.10'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '10.0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '10.0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: A middleware for Grape to add endpoint-specific throttling.
|
112
|
+
email:
|
113
|
+
- pierrelouis.gottfrois@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- .rspec
|
120
|
+
- .travis.yml
|
121
|
+
- Gemfile
|
122
|
+
- LICENSE.txt
|
123
|
+
- README.md
|
124
|
+
- Rakefile
|
125
|
+
- bin/console
|
126
|
+
- bin/setup
|
127
|
+
- grape-attack.gemspec
|
128
|
+
- lib/grape/attack.rb
|
129
|
+
- lib/grape/attack/adapters/memory.rb
|
130
|
+
- lib/grape/attack/adapters/redis.rb
|
131
|
+
- lib/grape/attack/configurable.rb
|
132
|
+
- lib/grape/attack/configuration.rb
|
133
|
+
- lib/grape/attack/counter.rb
|
134
|
+
- lib/grape/attack/exceptions.rb
|
135
|
+
- lib/grape/attack/extension.rb
|
136
|
+
- lib/grape/attack/limiter.rb
|
137
|
+
- lib/grape/attack/options.rb
|
138
|
+
- lib/grape/attack/request.rb
|
139
|
+
- lib/grape/attack/throttle.rb
|
140
|
+
- lib/grape/attack/version.rb
|
141
|
+
homepage: ''
|
142
|
+
licenses:
|
143
|
+
- MIT
|
144
|
+
metadata: {}
|
145
|
+
post_install_message:
|
146
|
+
rdoc_options: []
|
147
|
+
require_paths:
|
148
|
+
- lib
|
149
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - '>='
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - '>='
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
requirements: []
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 2.4.6
|
162
|
+
signing_key:
|
163
|
+
specification_version: 4
|
164
|
+
summary: A middleware for Grape to add endpoint-specific throttling.
|
165
|
+
test_files: []
|