rack-ratelimit 1.0.1 → 1.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.
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