improved-rack-throttle 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -1
- data/Gemfile.lock +2 -2
- data/improved-rack-throttle.gemspec +21 -19
- data/lib/rack/throttle/{daily.rb → limiters/daily.rb} +0 -0
- data/lib/rack/throttle/{hourly.rb → limiters/hourly.rb} +0 -0
- data/lib/rack/throttle/{interval.rb → limiters/interval.rb} +0 -0
- data/lib/rack/throttle/{limiter.rb → limiters/limiter.rb} +0 -1
- data/lib/rack/throttle/limiters/sliding_window.rb +79 -0
- data/lib/rack/throttle/{time_window.rb → limiters/time_window.rb} +0 -0
- data/lib/rack/throttle/{matcher.rb → matchers/matcher.rb} +0 -0
- data/lib/rack/throttle/version.rb +1 -1
- data/lib/rack/throttle.rb +10 -9
- data/spec/{daily_spec.rb → limiters/daily_spec.rb} +1 -1
- data/spec/{hourly_spec.rb → limiters/hourly_spec.rb} +1 -1
- data/spec/{interval_spec.rb → limiters/interval_spec.rb} +1 -1
- data/spec/{limiter_spec.rb → limiters/limiter_spec.rb} +1 -1
- data/spec/limiters/sliding_window_spec.rb +67 -0
- data/spec/{method_matcher_spec.rb → matchers/method_matcher_spec.rb} +1 -3
- data/spec/{url_matcher_spec.rb → matchers/url_matcher_spec.rb} +1 -1
- data/spec/{user_agent_matcher_spec.rb → matchers/user_agent_matcher_spec.rb} +2 -2
- metadata +33 -31
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -9,7 +9,7 @@ GEM
|
|
9
9
|
rake
|
10
10
|
rdoc
|
11
11
|
json (1.7.5)
|
12
|
-
rack (1.
|
12
|
+
rack (1.4.1)
|
13
13
|
rack-test (0.6.2)
|
14
14
|
rack (>= 1.0)
|
15
15
|
rake (0.9.2.2)
|
@@ -31,7 +31,7 @@ PLATFORMS
|
|
31
31
|
|
32
32
|
DEPENDENCIES
|
33
33
|
jeweler
|
34
|
-
rack (
|
34
|
+
rack (>= 1.0.0)
|
35
35
|
rack-test (~> 0.6.2)
|
36
36
|
rake
|
37
37
|
rspec (~> 2.11.0)
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "improved-rack-throttle"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Ben Somers", "Arto Bendiken", "Brendon Murphy"]
|
12
|
-
s.date = "2012-10-
|
12
|
+
s.date = "2012-10-05"
|
13
13
|
s.description = "Rack middleware for rate-limiting incoming HTTP requests."
|
14
14
|
s.email = "somers.ben@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -31,24 +31,26 @@ Gem::Specification.new do |s|
|
|
31
31
|
"etc/redis.ru",
|
32
32
|
"improved-rack-throttle.gemspec",
|
33
33
|
"lib/rack/throttle.rb",
|
34
|
-
"lib/rack/throttle/daily.rb",
|
35
|
-
"lib/rack/throttle/hourly.rb",
|
36
|
-
"lib/rack/throttle/interval.rb",
|
37
|
-
"lib/rack/throttle/limiter.rb",
|
38
|
-
"lib/rack/throttle/
|
34
|
+
"lib/rack/throttle/limiters/daily.rb",
|
35
|
+
"lib/rack/throttle/limiters/hourly.rb",
|
36
|
+
"lib/rack/throttle/limiters/interval.rb",
|
37
|
+
"lib/rack/throttle/limiters/limiter.rb",
|
38
|
+
"lib/rack/throttle/limiters/sliding_window.rb",
|
39
|
+
"lib/rack/throttle/limiters/time_window.rb",
|
40
|
+
"lib/rack/throttle/matchers/matcher.rb",
|
39
41
|
"lib/rack/throttle/matchers/method_matcher.rb",
|
40
42
|
"lib/rack/throttle/matchers/url_matcher.rb",
|
41
43
|
"lib/rack/throttle/matchers/user_agent_matcher.rb",
|
42
|
-
"lib/rack/throttle/time_window.rb",
|
43
44
|
"lib/rack/throttle/version.rb",
|
44
|
-
"spec/daily_spec.rb",
|
45
|
-
"spec/hourly_spec.rb",
|
46
|
-
"spec/interval_spec.rb",
|
47
|
-
"spec/limiter_spec.rb",
|
48
|
-
"spec/
|
49
|
-
"spec/
|
50
|
-
"spec/url_matcher_spec.rb",
|
51
|
-
"spec/user_agent_matcher_spec.rb"
|
45
|
+
"spec/limiters/daily_spec.rb",
|
46
|
+
"spec/limiters/hourly_spec.rb",
|
47
|
+
"spec/limiters/interval_spec.rb",
|
48
|
+
"spec/limiters/limiter_spec.rb",
|
49
|
+
"spec/limiters/sliding_window_spec.rb",
|
50
|
+
"spec/matchers/method_matcher_spec.rb",
|
51
|
+
"spec/matchers/url_matcher_spec.rb",
|
52
|
+
"spec/matchers/user_agent_matcher_spec.rb",
|
53
|
+
"spec/spec_helper.rb"
|
52
54
|
]
|
53
55
|
s.homepage = "http://github.com/bensomers/improved-rack-throttle"
|
54
56
|
s.licenses = ["Public Domain"]
|
@@ -60,7 +62,7 @@ Gem::Specification.new do |s|
|
|
60
62
|
s.specification_version = 3
|
61
63
|
|
62
64
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
63
|
-
s.add_runtime_dependency(%q<rack>, ["
|
65
|
+
s.add_runtime_dependency(%q<rack>, [">= 1.0.0"])
|
64
66
|
s.add_development_dependency(%q<timecop>, ["~> 0.5.2"])
|
65
67
|
s.add_development_dependency(%q<rack-test>, ["~> 0.6.2"])
|
66
68
|
s.add_development_dependency(%q<rspec>, ["~> 2.11.0"])
|
@@ -68,7 +70,7 @@ Gem::Specification.new do |s|
|
|
68
70
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
69
71
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
70
72
|
else
|
71
|
-
s.add_dependency(%q<rack>, ["
|
73
|
+
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
72
74
|
s.add_dependency(%q<timecop>, ["~> 0.5.2"])
|
73
75
|
s.add_dependency(%q<rack-test>, ["~> 0.6.2"])
|
74
76
|
s.add_dependency(%q<rspec>, ["~> 2.11.0"])
|
@@ -77,7 +79,7 @@ Gem::Specification.new do |s|
|
|
77
79
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
78
80
|
end
|
79
81
|
else
|
80
|
-
s.add_dependency(%q<rack>, ["
|
82
|
+
s.add_dependency(%q<rack>, [">= 1.0.0"])
|
81
83
|
s.add_dependency(%q<timecop>, ["~> 0.5.2"])
|
82
84
|
s.add_dependency(%q<rack-test>, ["~> 0.6.2"])
|
83
85
|
s.add_dependency(%q<rspec>, ["~> 2.11.0"])
|
File without changes
|
File without changes
|
File without changes
|
@@ -20,7 +20,6 @@ module Rack; module Throttle
|
|
20
20
|
# @option options [String] :key_prefix (nil)
|
21
21
|
# @option options [Integer] :code (403)
|
22
22
|
# @option options [String] :message ("Rate Limit Exceeded")
|
23
|
-
# @option options [Regexp] :url_rule (nil)
|
24
23
|
def initialize(app, options = {})
|
25
24
|
rules = options.delete(:rules) || {}
|
26
25
|
@app, @options, @matchers = app, options, []
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Rack; module Throttle
|
2
|
+
##
|
3
|
+
# This rate limiter strategy throttles the application with
|
4
|
+
# a sliding window (implemented as a leaky bucket). It operates
|
5
|
+
# on second-level resolution. It takes :burst and :average
|
6
|
+
# options, which correspond to the maximum size of a traffic
|
7
|
+
# burst, and the maximum allowed average traffic level.
|
8
|
+
class SlidingWindow < Limiter
|
9
|
+
##
|
10
|
+
# @param [#call] app
|
11
|
+
# @param [Hash{Symbol => Object}] options
|
12
|
+
# @option options [Integer] :burst 5
|
13
|
+
# @option options [Float] :average 1
|
14
|
+
def initialize(app, options = {})
|
15
|
+
super
|
16
|
+
options[:burst] ||= 5
|
17
|
+
options[:average] ||= 1
|
18
|
+
end
|
19
|
+
|
20
|
+
##
|
21
|
+
# Returns `true` if sufficient time (equal to or more than
|
22
|
+
# {#minimum_interval}) has passed since the last request and the given
|
23
|
+
# present `request`.
|
24
|
+
#
|
25
|
+
# @param [Rack::Request] request
|
26
|
+
# @return [Boolean]
|
27
|
+
def allowed?(request)
|
28
|
+
t1 = request_start_time(request)
|
29
|
+
key = cache_key(request)
|
30
|
+
bucket = cache_get(key) rescue nil
|
31
|
+
bucket ||= LeakyBucket.new(options[:burst], options[:average])
|
32
|
+
bucket.maximum, bucket.outflow = options[:burst], options[:average]
|
33
|
+
bucket.leak!
|
34
|
+
bucket.increment!
|
35
|
+
allowed = !bucket.full?
|
36
|
+
begin
|
37
|
+
cache_set(key, bucket)
|
38
|
+
allowed
|
39
|
+
rescue StandardError => e
|
40
|
+
allowed = true
|
41
|
+
# If an error occurred while trying to update the timestamp stored
|
42
|
+
# in the cache, we will fall back to allowing the request through.
|
43
|
+
# This prevents the Rack application blowing up merely due to a
|
44
|
+
# backend cache server (Memcached, Redis, etc.) being offline.
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class LeakyBucket
|
49
|
+
attr_accessor :maximum, :outflow
|
50
|
+
attr_reader :count, :last_touched
|
51
|
+
|
52
|
+
def initialize(maximum, outflow)
|
53
|
+
@maximum, @outflow = maximum, outflow
|
54
|
+
@count, @last_touched = 0, Time.now
|
55
|
+
end
|
56
|
+
|
57
|
+
def leak!
|
58
|
+
t = Time.now
|
59
|
+
time = t - last_touched
|
60
|
+
loss = (outflow * time).to_f
|
61
|
+
if loss > 0
|
62
|
+
@count -= loss
|
63
|
+
@last_touched = t
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def increment!
|
68
|
+
@count = 0 if count < 0
|
69
|
+
@count += 1
|
70
|
+
@count = maximum if count > maximum
|
71
|
+
end
|
72
|
+
|
73
|
+
def full?
|
74
|
+
count == maximum
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
end; end
|
File without changes
|
File without changes
|
data/lib/rack/throttle.rb
CHANGED
@@ -2,15 +2,16 @@ require 'rack'
|
|
2
2
|
|
3
3
|
module Rack
|
4
4
|
module Throttle
|
5
|
-
autoload :Limiter,
|
6
|
-
autoload :Interval,
|
7
|
-
autoload :TimeWindow,
|
8
|
-
autoload :Daily,
|
9
|
-
autoload :Hourly,
|
10
|
-
autoload :
|
11
|
-
autoload :
|
12
|
-
autoload :
|
13
|
-
autoload :
|
5
|
+
autoload :Limiter, 'rack/throttle/limiters/limiter'
|
6
|
+
autoload :Interval, 'rack/throttle/limiters/interval'
|
7
|
+
autoload :TimeWindow, 'rack/throttle/limiters/time_window'
|
8
|
+
autoload :Daily, 'rack/throttle/limiters/daily'
|
9
|
+
autoload :Hourly, 'rack/throttle/limiters/hourly'
|
10
|
+
autoload :SlidingWindow, 'rack/throttle/limiters/sliding_window'
|
11
|
+
autoload :VERSION, 'rack/throttle/version'
|
12
|
+
autoload :Matcher, 'rack/throttle/matchers/matcher'
|
13
|
+
autoload :UrlMatcher, 'rack/throttle/matchers/url_matcher'
|
14
|
+
autoload :MethodMatcher, 'rack/throttle/matchers/method_matcher'
|
14
15
|
autoload :UserAgentMatcher, 'rack/throttle/matchers/user_agent_matcher'
|
15
16
|
end
|
16
17
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
|
+
|
3
|
+
describe Rack::Throttle::SlidingWindow do
|
4
|
+
include Rack::Test::Methods
|
5
|
+
|
6
|
+
def app
|
7
|
+
@target_app ||= example_target_app
|
8
|
+
@app ||= Rack::Throttle::SlidingWindow.new(@target_app, :burst => 2, :average => 1)
|
9
|
+
end
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
@time = Time.now
|
13
|
+
Timecop.freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:each) do
|
17
|
+
Timecop.return
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should allow the request if the source has not been seen at all" do
|
21
|
+
get "/foo"
|
22
|
+
last_response.body.should show_allowed_response
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should allow the request if the rate is above-average but within the burst rule" do
|
26
|
+
Timecop.freeze(@time) { get "/foo" }
|
27
|
+
Timecop.freeze(@time + 0.5) { get "/foo" }
|
28
|
+
last_response.body.should show_allowed_response
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not allow the request if the rate is greater than the burst rule" do
|
32
|
+
Timecop.freeze(@time) { get "/foo" }
|
33
|
+
Timecop.freeze(@time + 0.3) { get "/foo" }
|
34
|
+
Timecop.freeze(@time + 0.6) { get "/foo" }
|
35
|
+
last_response.body.should show_throttled_response
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should allow the request if the rate is less than the average" do
|
39
|
+
Timecop.freeze(@time) { get "/foo" }
|
40
|
+
Timecop.freeze(@time + 0.5) { get "/foo" }
|
41
|
+
Timecop.freeze(@time + 2) { get "/foo" }
|
42
|
+
|
43
|
+
last_response.body.should show_allowed_response
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should not allow the request if the rate is more than the average" do
|
47
|
+
Timecop.freeze(@time) { get "/foo" }
|
48
|
+
Timecop.freeze(@time + 0.5) { get "/foo" }
|
49
|
+
Timecop.freeze(@time + 1) { get "/foo" }
|
50
|
+
Timecop.freeze(@time + 1.5) { get "/foo" }
|
51
|
+
|
52
|
+
last_response.body.should show_throttled_response
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should gracefully allow the request if the cache bombs on getting" do
|
56
|
+
app.should_receive(:cache_get).and_raise(StandardError)
|
57
|
+
get "/foo"
|
58
|
+
last_response.body.should show_allowed_response
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should gracefully allow the request if the cache bombs on setting" do
|
62
|
+
app.should_receive(:cache_set).and_raise(StandardError)
|
63
|
+
get "/foo"
|
64
|
+
last_response.body.should show_allowed_response
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__)
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
describe Rack::Throttle::MethodMatcher do
|
4
4
|
include Rack::Test::Methods
|
@@ -25,5 +25,3 @@ describe Rack::Throttle::MethodMatcher do
|
|
25
25
|
app.send(:cache_key, last_request).should == "127.0.0.1:meth-post"
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
29
|
-
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require File.dirname(__FILE__)
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper')
|
2
2
|
|
3
3
|
describe Rack::Throttle::UserAgentMatcher do
|
4
4
|
include Rack::Test::Methods
|
@@ -13,7 +13,7 @@ describe Rack::Throttle::UserAgentMatcher do
|
|
13
13
|
get "/foo"
|
14
14
|
last_response.body.should show_allowed_response
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
it "should check if the path matches the rule" do
|
18
18
|
app.should_receive(:allowed?).and_return(false)
|
19
19
|
header 'User-Agent', 'blahdeblah GoogleBot owns your soul'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: improved-rack-throttle
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,22 +11,22 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-10-
|
14
|
+
date: 2012-10-05 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rack
|
18
|
-
requirement: &
|
18
|
+
requirement: &70296124961520 !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
21
|
-
- -
|
21
|
+
- - ! '>='
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 1.0.0
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
|
-
version_requirements: *
|
26
|
+
version_requirements: *70296124961520
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: timecop
|
29
|
-
requirement: &
|
29
|
+
requirement: &70296124960800 !ruby/object:Gem::Requirement
|
30
30
|
none: false
|
31
31
|
requirements:
|
32
32
|
- - ~>
|
@@ -34,10 +34,10 @@ dependencies:
|
|
34
34
|
version: 0.5.2
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
|
-
version_requirements: *
|
37
|
+
version_requirements: *70296124960800
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
39
|
name: rack-test
|
40
|
-
requirement: &
|
40
|
+
requirement: &70296124960000 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
@@ -45,10 +45,10 @@ dependencies:
|
|
45
45
|
version: 0.6.2
|
46
46
|
type: :development
|
47
47
|
prerelease: false
|
48
|
-
version_requirements: *
|
48
|
+
version_requirements: *70296124960000
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
50
|
name: rspec
|
51
|
-
requirement: &
|
51
|
+
requirement: &70296124959520 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
53
53
|
requirements:
|
54
54
|
- - ~>
|
@@ -56,10 +56,10 @@ dependencies:
|
|
56
56
|
version: 2.11.0
|
57
57
|
type: :development
|
58
58
|
prerelease: false
|
59
|
-
version_requirements: *
|
59
|
+
version_requirements: *70296124959520
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: yard
|
62
|
-
requirement: &
|
62
|
+
requirement: &70296124958980 !ruby/object:Gem::Requirement
|
63
63
|
none: false
|
64
64
|
requirements:
|
65
65
|
- - ! '>='
|
@@ -67,10 +67,10 @@ dependencies:
|
|
67
67
|
version: 0.5.5
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
|
-
version_requirements: *
|
70
|
+
version_requirements: *70296124958980
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: rake
|
73
|
-
requirement: &
|
73
|
+
requirement: &70296124958220 !ruby/object:Gem::Requirement
|
74
74
|
none: false
|
75
75
|
requirements:
|
76
76
|
- - ! '>='
|
@@ -78,10 +78,10 @@ dependencies:
|
|
78
78
|
version: '0'
|
79
79
|
type: :development
|
80
80
|
prerelease: false
|
81
|
-
version_requirements: *
|
81
|
+
version_requirements: *70296124958220
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
83
|
name: jeweler
|
84
|
-
requirement: &
|
84
|
+
requirement: &70296124957640 !ruby/object:Gem::Requirement
|
85
85
|
none: false
|
86
86
|
requirements:
|
87
87
|
- - ! '>='
|
@@ -89,7 +89,7 @@ dependencies:
|
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
|
-
version_requirements: *
|
92
|
+
version_requirements: *70296124957640
|
93
93
|
description: Rack middleware for rate-limiting incoming HTTP requests.
|
94
94
|
email: somers.ben@gmail.com
|
95
95
|
executables: []
|
@@ -112,24 +112,26 @@ files:
|
|
112
112
|
- etc/redis.ru
|
113
113
|
- improved-rack-throttle.gemspec
|
114
114
|
- lib/rack/throttle.rb
|
115
|
-
- lib/rack/throttle/daily.rb
|
116
|
-
- lib/rack/throttle/hourly.rb
|
117
|
-
- lib/rack/throttle/interval.rb
|
118
|
-
- lib/rack/throttle/limiter.rb
|
119
|
-
- lib/rack/throttle/
|
115
|
+
- lib/rack/throttle/limiters/daily.rb
|
116
|
+
- lib/rack/throttle/limiters/hourly.rb
|
117
|
+
- lib/rack/throttle/limiters/interval.rb
|
118
|
+
- lib/rack/throttle/limiters/limiter.rb
|
119
|
+
- lib/rack/throttle/limiters/sliding_window.rb
|
120
|
+
- lib/rack/throttle/limiters/time_window.rb
|
121
|
+
- lib/rack/throttle/matchers/matcher.rb
|
120
122
|
- lib/rack/throttle/matchers/method_matcher.rb
|
121
123
|
- lib/rack/throttle/matchers/url_matcher.rb
|
122
124
|
- lib/rack/throttle/matchers/user_agent_matcher.rb
|
123
|
-
- lib/rack/throttle/time_window.rb
|
124
125
|
- lib/rack/throttle/version.rb
|
125
|
-
- spec/daily_spec.rb
|
126
|
-
- spec/hourly_spec.rb
|
127
|
-
- spec/interval_spec.rb
|
128
|
-
- spec/limiter_spec.rb
|
129
|
-
- spec/
|
126
|
+
- spec/limiters/daily_spec.rb
|
127
|
+
- spec/limiters/hourly_spec.rb
|
128
|
+
- spec/limiters/interval_spec.rb
|
129
|
+
- spec/limiters/limiter_spec.rb
|
130
|
+
- spec/limiters/sliding_window_spec.rb
|
131
|
+
- spec/matchers/method_matcher_spec.rb
|
132
|
+
- spec/matchers/url_matcher_spec.rb
|
133
|
+
- spec/matchers/user_agent_matcher_spec.rb
|
130
134
|
- spec/spec_helper.rb
|
131
|
-
- spec/url_matcher_spec.rb
|
132
|
-
- spec/user_agent_matcher_spec.rb
|
133
135
|
homepage: http://github.com/bensomers/improved-rack-throttle
|
134
136
|
licenses:
|
135
137
|
- Public Domain
|
@@ -145,7 +147,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
145
147
|
version: '0'
|
146
148
|
segments:
|
147
149
|
- 0
|
148
|
-
hash:
|
150
|
+
hash: 3964473901145440251
|
149
151
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
152
|
none: false
|
151
153
|
requirements:
|