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.
- checksums.yaml +4 -4
- data/lib/rack/ratelimit.rb +39 -7
- metadata +20 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 21689cc38cfc536f64e5edb837ca4ebbf3953d85
|
4
|
+
data.tar.gz: 9fca02872a124ec27453904600765afe10c000cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffc295ecfec0dcbeb293452e1d14983c3448d0b78fd3343e953b2661695857462f154ca7d0069eb1974c63e03b8dd527d38a0fa7271044011366a6754e4c7823
|
7
|
+
data.tar.gz: 06758a18ce4773e37c8ce7503f700e10f0f11dfb76995f2158a167d3be97c4b4ab21410fb96f88507f6a22fef17bdc4b654cd66c42bd14c3c3e53d9f8793d8b7
|
data/lib/rack/ratelimit.rb
CHANGED
@@ -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
|
12
|
+
# * Fast, low-overhead implementation using counters per time window:
|
14
13
|
# timeslice = window * ceiling(current time / window)
|
15
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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 =
|
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
|
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
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Jeremy
|
7
|
+
- Jeremy Daer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
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: :
|
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:
|
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
|
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
|