rack-ratelimit 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rack/ratelimit.rb +39 -7
  3. metadata +20 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e01063bca7c6d18ab0206611063a339dc7ddfad6
4
- data.tar.gz: ffb944f15efc8239948306e4948f25c8d488e07c
3
+ metadata.gz: 21689cc38cfc536f64e5edb837ca4ebbf3953d85
4
+ data.tar.gz: 9fca02872a124ec27453904600765afe10c000cc
5
5
  SHA512:
6
- metadata.gz: b64f78b469c570ade7cb6f03e301c70550adb7486b3e5a2e36de522db35d80ce769ddd7f18e18274c6a5a6da2a2a5224244268a2a6378e5e9f8e91228ab6e7b7
7
- data.tar.gz: 0b4929fb74cd3da4a727e1ef5c0e5fbf160704e06be4e4e639df7be98e28f1dd6dada78918bd72cd2ee85d17efceb57cb123223777fda4b83a59dbcb27f7c763
6
+ metadata.gz: ffc295ecfec0dcbeb293452e1d14983c3448d0b78fd3343e953b2661695857462f154ca7d0069eb1974c63e03b8dd527d38a0fa7271044011366a6754e4c7823
7
+ data.tar.gz: 06758a18ce4773e37c8ce7503f700e10f0f11dfb76995f2158a167d3be97c4b4ab21410fb96f88507f6a22fef17bdc4b654cd66c42bd14c3c3e53d9f8793d8b7
@@ -1,4 +1,3 @@
1
- require 'dalli'
2
1
  require 'logger'
3
2
  require 'time'
4
3
 
@@ -10,9 +9,9 @@ module Rack
10
9
  # * Apply each rate limit by request characteristics: IP, subdomain, OAuth2 token, etc.
11
10
  # * Flexible time window to limit burst traffic vs hourly or daily traffic:
12
11
  # 100 requests per 10 sec, 500 req/minute, 10000 req/hour, etc.
13
- # * Fast, low-overhead implementation using memcache counters per time window:
12
+ # * Fast, low-overhead implementation using counters per time window:
14
13
  # timeslice = window * ceiling(current time / window)
15
- # memcache.incr(counter for timeslice)
14
+ # store.incr(timeslice)
16
15
  class Ratelimit
17
16
  # Takes a block that classifies requests for rate limiting. Given a
18
17
  # Rack env, return a string such as IP address, API token, etc. If the
@@ -21,7 +20,12 @@ module Rack
21
20
  #
22
21
  # Required configuration:
23
22
  # rate: an array of [max requests, period in seconds]: [500, 5.minutes]
24
- # cache: a Dalli::Client instance, or an object that quacks like it.
23
+ # and one of
24
+ # cache: a Dalli::Client instance
25
+ # redis: a Redis instance
26
+ # counter: Your own custom counter. Must respond to
27
+ # `#increment(classification_string, end_of_time_window_timestamp)`
28
+ # and return the counter value after increment.
25
29
  #
26
30
  # Optional configuration:
27
31
  # name: name of the rate limiter. Defaults to 'HTTP'. Used in messages.
@@ -51,7 +55,7 @@ module Rack
51
55
  # use(Rack::Ratelimit, name: 'API',
52
56
  # conditions: ->(env) { env['REMOTE_USER'] },
53
57
  # rate: [1000, 1.hour],
54
- # cache: Dalli::Client.new,
58
+ # redis: Redis.new(ratelimit_redis_config),
55
59
  # logger: Rails.logger) { |env| env['REMOTE_USER'] }
56
60
  def initialize(app, options, &classifier)
57
61
  @app, @classifier = app, classifier
@@ -61,7 +65,17 @@ module Rack
61
65
  @max, @period = options.fetch(:rate)
62
66
  @status = options.fetch(:status, 429)
63
67
 
64
- @counter = Counter.new(options.fetch(:cache), @name, @period)
68
+ @counter =
69
+ if counter = options[:counter]
70
+ raise ArgumentError, 'Counter must respond to #increment' unless counter.respond_to?(:increment)
71
+ counter
72
+ elsif cache = options[:cache]
73
+ MemcachedCounter.new(cache, @name, @period)
74
+ elsif redis = options[:redis]
75
+ RedisCounter.new(redis, @name, @period)
76
+ else
77
+ raise ArgumentError, ':cache, :redis, or :counter is required'
78
+ end
65
79
 
66
80
  @logger = options[:logger]
67
81
  @error_message = options.fetch(:error_message, "#{@name} rate limit exceeded. Please wait #{@period} seconds then retry your request.")
@@ -140,7 +154,7 @@ module Rack
140
154
  end
141
155
  end
142
156
 
143
- class Counter
157
+ class MemcachedCounter
144
158
  def initialize(cache, name, period)
145
159
  @cache, @name, @period = cache, name, period
146
160
  end
@@ -163,5 +177,23 @@ module Rack
163
177
  end
164
178
  end
165
179
  end
180
+
181
+ class RedisCounter
182
+ def initialize(redis, name, period)
183
+ @redis, @name, @period = redis, name, period
184
+ end
185
+
186
+ # Increment the request counter and return the current count.
187
+ def increment(classification, timestamp)
188
+ key = 'rack-ratelimit/%s/%s/%i' % [@name, classification, timestamp]
189
+
190
+ # Returns [count, expire_ok] response for each multi command.
191
+ # Return the first, the count.
192
+ @redis.multi do |redis|
193
+ redis.incr key
194
+ redis.expire key, @period
195
+ end.first
196
+ end
197
+ end
166
198
  end
167
199
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-ratelimit
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
- - Jeremy Kemper
7
+ - Jeremy Daer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-21 00:00:00.000000000 Z
11
+ date: 2016-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -31,7 +31,21 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
- type: :runtime
34
+ type: :development
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: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
@@ -67,7 +81,7 @@ dependencies:
67
81
  - !ruby/object:Gem::Version
68
82
  version: 5.3.0
69
83
  description:
70
- email: jeremy@bitsweat.net
84
+ email: jeremydaer@gmail.com
71
85
  executables: []
72
86
  extensions: []
73
87
  extra_rdoc_files: []
@@ -94,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
108
  version: '0'
95
109
  requirements: []
96
110
  rubyforge_project:
97
- rubygems_version: 2.2.0
111
+ rubygems_version: 2.5.2
98
112
  signing_key:
99
113
  specification_version: 4
100
114
  summary: Flexible rate limits for your Rack apps