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.
- 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
|