rack-attack 5.0.1 → 5.4.2
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 +5 -5
- data/README.md +190 -94
- data/Rakefile +11 -4
- data/bin/setup +8 -0
- data/lib/rack/attack.rb +83 -51
- data/lib/rack/attack/allow2ban.rb +2 -1
- data/lib/rack/attack/blocklist.rb +0 -1
- data/lib/rack/attack/cache.rb +24 -5
- data/lib/rack/attack/check.rb +6 -8
- data/lib/rack/attack/fail2ban.rb +2 -1
- data/lib/rack/attack/path_normalizer.rb +6 -11
- data/lib/rack/attack/safelist.rb +0 -1
- data/lib/rack/attack/store_proxy.rb +3 -12
- data/lib/rack/attack/store_proxy/dalli_proxy.rb +2 -3
- data/lib/rack/attack/store_proxy/mem_cache_proxy.rb +4 -5
- data/lib/rack/attack/store_proxy/mem_cache_store_proxy.rb +19 -0
- data/lib/rack/attack/store_proxy/redis_cache_store_proxy.rb +35 -0
- data/lib/rack/attack/store_proxy/redis_proxy.rb +54 -0
- data/lib/rack/attack/store_proxy/redis_store_proxy.rb +5 -24
- data/lib/rack/attack/throttle.rb +16 -12
- data/lib/rack/attack/track.rb +3 -3
- data/lib/rack/attack/version.rb +1 -1
- data/spec/acceptance/allow2ban_spec.rb +71 -0
- data/spec/acceptance/blocking_ip_spec.rb +38 -0
- data/spec/acceptance/blocking_spec.rb +41 -0
- data/spec/acceptance/blocking_subnet_spec.rb +44 -0
- data/spec/acceptance/cache_store_config_for_allow2ban_spec.rb +126 -0
- data/spec/acceptance/cache_store_config_for_fail2ban_spec.rb +121 -0
- data/spec/acceptance/cache_store_config_for_throttle_spec.rb +48 -0
- data/spec/acceptance/cache_store_config_with_rails_spec.rb +31 -0
- data/spec/acceptance/customizing_blocked_response_spec.rb +41 -0
- data/spec/acceptance/customizing_throttled_response_spec.rb +59 -0
- data/spec/acceptance/extending_request_object_spec.rb +34 -0
- data/spec/acceptance/fail2ban_spec.rb +76 -0
- data/spec/acceptance/safelisting_ip_spec.rb +48 -0
- data/spec/acceptance/safelisting_spec.rb +53 -0
- data/spec/acceptance/safelisting_subnet_spec.rb +48 -0
- data/spec/acceptance/stores/active_support_dalli_store_spec.rb +19 -0
- data/spec/acceptance/stores/active_support_mem_cache_store_pooled_spec.rb +22 -0
- data/spec/acceptance/stores/active_support_mem_cache_store_spec.rb +18 -0
- data/spec/acceptance/stores/active_support_memory_store_spec.rb +16 -0
- data/spec/acceptance/stores/active_support_redis_cache_store_pooled_spec.rb +18 -0
- data/spec/acceptance/stores/active_support_redis_cache_store_spec.rb +18 -0
- data/spec/acceptance/stores/active_support_redis_store_spec.rb +18 -0
- data/spec/acceptance/stores/connection_pool_dalli_client_spec.rb +22 -0
- data/spec/acceptance/stores/dalli_client_spec.rb +19 -0
- data/spec/acceptance/stores/redis_spec.rb +20 -0
- data/spec/acceptance/stores/redis_store_spec.rb +18 -0
- data/spec/acceptance/throttling_spec.rb +159 -0
- data/spec/acceptance/track_spec.rb +27 -0
- data/spec/acceptance/track_throttle_spec.rb +53 -0
- data/spec/allow2ban_spec.rb +9 -8
- data/spec/fail2ban_spec.rb +11 -9
- data/spec/integration/offline_spec.rb +21 -23
- data/spec/rack_attack_dalli_proxy_spec.rb +0 -2
- data/spec/rack_attack_request_spec.rb +1 -1
- data/spec/rack_attack_spec.rb +13 -14
- data/spec/rack_attack_throttle_spec.rb +28 -18
- data/spec/rack_attack_track_spec.rb +11 -8
- data/spec/spec_helper.rb +35 -14
- data/spec/support/cache_store_helper.rb +82 -0
- metadata +150 -65
- data/spec/integration/rack_attack_cache_spec.rb +0 -122
data/Rakefile
CHANGED
|
@@ -2,6 +2,9 @@ require "rubygems"
|
|
|
2
2
|
require "bundler/setup"
|
|
3
3
|
require 'bundler/gem_tasks'
|
|
4
4
|
require 'rake/testtask'
|
|
5
|
+
require "rubocop/rake_task"
|
|
6
|
+
|
|
7
|
+
RuboCop::RakeTask.new
|
|
5
8
|
|
|
6
9
|
namespace :test do
|
|
7
10
|
Rake::TestTask.new(:units) do |t|
|
|
@@ -10,11 +13,15 @@ namespace :test do
|
|
|
10
13
|
|
|
11
14
|
Rake::TestTask.new(:integration) do |t|
|
|
12
15
|
t.pattern = "spec/integration/*_spec.rb"
|
|
13
|
-
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Rake::TestTask.new(:acceptance) do |t|
|
|
19
|
+
t.pattern = "spec/acceptance/**/*_spec.rb"
|
|
14
20
|
end
|
|
15
21
|
end
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
Rake::TestTask.new(:test) do |t|
|
|
24
|
+
t.pattern = "spec/**/*_spec.rb"
|
|
25
|
+
end
|
|
19
26
|
|
|
20
|
-
task :default => :test
|
|
27
|
+
task :default => [:rubocop, :test]
|
data/bin/setup
ADDED
data/lib/rack/attack.rb
CHANGED
|
@@ -1,24 +1,30 @@
|
|
|
1
1
|
require 'rack'
|
|
2
2
|
require 'forwardable'
|
|
3
|
+
require 'rack/attack/path_normalizer'
|
|
4
|
+
require 'rack/attack/request'
|
|
5
|
+
require "ipaddr"
|
|
3
6
|
|
|
4
7
|
class Rack::Attack
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
autoload :
|
|
9
|
-
autoload :
|
|
10
|
-
autoload :
|
|
11
|
-
autoload :
|
|
12
|
-
autoload :
|
|
13
|
-
autoload :
|
|
14
|
-
autoload :
|
|
15
|
-
autoload :
|
|
16
|
-
autoload :
|
|
17
|
-
autoload :
|
|
18
|
-
autoload :
|
|
8
|
+
class MisconfiguredStoreError < StandardError; end
|
|
9
|
+
class MissingStoreError < StandardError; end
|
|
10
|
+
|
|
11
|
+
autoload :Cache, 'rack/attack/cache'
|
|
12
|
+
autoload :Check, 'rack/attack/check'
|
|
13
|
+
autoload :Throttle, 'rack/attack/throttle'
|
|
14
|
+
autoload :Safelist, 'rack/attack/safelist'
|
|
15
|
+
autoload :Blocklist, 'rack/attack/blocklist'
|
|
16
|
+
autoload :Track, 'rack/attack/track'
|
|
17
|
+
autoload :StoreProxy, 'rack/attack/store_proxy'
|
|
18
|
+
autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy'
|
|
19
|
+
autoload :MemCacheProxy, 'rack/attack/store_proxy/mem_cache_proxy'
|
|
20
|
+
autoload :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy'
|
|
21
|
+
autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy'
|
|
22
|
+
autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy'
|
|
23
|
+
autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy'
|
|
24
|
+
autoload :Fail2Ban, 'rack/attack/fail2ban'
|
|
25
|
+
autoload :Allow2Ban, 'rack/attack/allow2ban'
|
|
19
26
|
|
|
20
27
|
class << self
|
|
21
|
-
|
|
22
28
|
attr_accessor :notifier, :blocklisted_response, :throttled_response
|
|
23
29
|
|
|
24
30
|
def safelist(name, &block)
|
|
@@ -34,6 +40,18 @@ class Rack::Attack
|
|
|
34
40
|
self.blocklists[name] = Blocklist.new(name, block)
|
|
35
41
|
end
|
|
36
42
|
|
|
43
|
+
def blocklist_ip(ip_address)
|
|
44
|
+
@ip_blocklists ||= []
|
|
45
|
+
ip_blocklist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
|
46
|
+
@ip_blocklists << Blocklist.new(nil, ip_blocklist_proc)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def safelist_ip(ip_address)
|
|
50
|
+
@ip_safelists ||= []
|
|
51
|
+
ip_safelist_proc = lambda { |request| IPAddr.new(ip_address).include?(IPAddr.new(request.ip)) }
|
|
52
|
+
@ip_safelists << Safelist.new(nil, ip_safelist_proc)
|
|
53
|
+
end
|
|
54
|
+
|
|
37
55
|
def blacklist(name, &block)
|
|
38
56
|
warn "[DEPRECATION] 'Rack::Attack.blacklist' is deprecated. Please use 'blocklist' instead."
|
|
39
57
|
blocklist(name, &block)
|
|
@@ -47,9 +65,12 @@ class Rack::Attack
|
|
|
47
65
|
self.tracks[name] = Track.new(name, options, block)
|
|
48
66
|
end
|
|
49
67
|
|
|
50
|
-
def safelists;
|
|
68
|
+
def safelists; @safelists ||= {}; end
|
|
69
|
+
|
|
51
70
|
def blocklists; @blocklists ||= {}; end
|
|
71
|
+
|
|
52
72
|
def throttles; @throttles ||= {}; end
|
|
73
|
+
|
|
53
74
|
def tracks; @tracks ||= {}; end
|
|
54
75
|
|
|
55
76
|
def whitelists
|
|
@@ -62,70 +83,84 @@ class Rack::Attack
|
|
|
62
83
|
blocklists
|
|
63
84
|
end
|
|
64
85
|
|
|
65
|
-
def safelisted?(
|
|
66
|
-
|
|
67
|
-
safelist
|
|
68
|
-
end
|
|
86
|
+
def safelisted?(request)
|
|
87
|
+
ip_safelists.any? { |safelist| safelist.matched_by?(request) } ||
|
|
88
|
+
safelists.any? { |_name, safelist| safelist.matched_by?(request) }
|
|
69
89
|
end
|
|
70
90
|
|
|
71
|
-
def whitelisted?(
|
|
91
|
+
def whitelisted?(request)
|
|
72
92
|
warn "[DEPRECATION] 'Rack::Attack.whitelisted?' is deprecated. Please use 'safelisted?' instead."
|
|
73
|
-
safelisted?(
|
|
93
|
+
safelisted?(request)
|
|
74
94
|
end
|
|
75
95
|
|
|
76
|
-
def blocklisted?(
|
|
77
|
-
|
|
78
|
-
blocklist
|
|
79
|
-
end
|
|
96
|
+
def blocklisted?(request)
|
|
97
|
+
ip_blocklists.any? { |blocklist| blocklist.matched_by?(request) } ||
|
|
98
|
+
blocklists.any? { |_name, blocklist| blocklist.matched_by?(request) }
|
|
80
99
|
end
|
|
81
100
|
|
|
82
|
-
def blacklisted?(
|
|
101
|
+
def blacklisted?(request)
|
|
83
102
|
warn "[DEPRECATION] 'Rack::Attack.blacklisted?' is deprecated. Please use 'blocklisted?' instead."
|
|
84
|
-
blocklisted?(
|
|
103
|
+
blocklisted?(request)
|
|
85
104
|
end
|
|
86
105
|
|
|
87
|
-
def throttled?(
|
|
88
|
-
throttles.any? do |
|
|
89
|
-
throttle
|
|
106
|
+
def throttled?(request)
|
|
107
|
+
throttles.any? do |_name, throttle|
|
|
108
|
+
throttle.matched_by?(request)
|
|
90
109
|
end
|
|
91
110
|
end
|
|
92
111
|
|
|
93
|
-
def tracked?(
|
|
94
|
-
tracks.each_value do |
|
|
95
|
-
|
|
112
|
+
def tracked?(request)
|
|
113
|
+
tracks.each_value do |track|
|
|
114
|
+
track.matched_by?(request)
|
|
96
115
|
end
|
|
97
116
|
end
|
|
98
117
|
|
|
99
|
-
def instrument(
|
|
100
|
-
notifier.instrument('rack.attack',
|
|
118
|
+
def instrument(request)
|
|
119
|
+
notifier.instrument('rack.attack', request) if notifier
|
|
101
120
|
end
|
|
102
121
|
|
|
103
122
|
def cache
|
|
104
123
|
@cache ||= Cache.new
|
|
105
124
|
end
|
|
106
125
|
|
|
107
|
-
def
|
|
126
|
+
def clear_configuration
|
|
108
127
|
@safelists, @blocklists, @throttles, @tracks = {}, {}, {}, {}
|
|
128
|
+
@ip_blocklists = []
|
|
129
|
+
@ip_safelists = []
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def clear!
|
|
133
|
+
warn "[DEPRECATION] Rack::Attack.clear! is deprecated. Please use Rack::Attack.clear_configuration instead"
|
|
134
|
+
clear_configuration
|
|
109
135
|
end
|
|
110
136
|
|
|
111
137
|
def blacklisted_response=(res)
|
|
112
138
|
warn "[DEPRECATION] 'Rack::Attack.blacklisted_response=' is deprecated. Please use 'blocklisted_response=' instead."
|
|
113
|
-
self.blocklisted_response=
|
|
139
|
+
self.blocklisted_response = res
|
|
114
140
|
end
|
|
115
141
|
|
|
116
142
|
def blacklisted_response
|
|
117
143
|
warn "[DEPRECATION] 'Rack::Attack.blacklisted_response' is deprecated. Please use 'blocklisted_response' instead."
|
|
118
|
-
|
|
144
|
+
blocklisted_response
|
|
119
145
|
end
|
|
120
146
|
|
|
147
|
+
private
|
|
148
|
+
|
|
149
|
+
def ip_blocklists
|
|
150
|
+
@ip_blocklists ||= []
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def ip_safelists
|
|
154
|
+
@ip_safelists ||= []
|
|
155
|
+
end
|
|
121
156
|
end
|
|
122
157
|
|
|
123
158
|
# Set defaults
|
|
124
159
|
@notifier = ActiveSupport::Notifications if defined?(ActiveSupport::Notifications)
|
|
125
|
-
@blocklisted_response = lambda {|
|
|
126
|
-
@throttled_response = lambda {|env|
|
|
160
|
+
@blocklisted_response = lambda { |_env| [403, { 'Content-Type' => 'text/plain' }, ["Forbidden\n"]] }
|
|
161
|
+
@throttled_response = lambda { |env|
|
|
127
162
|
retry_after = (env['rack.attack.match_data'] || {})[:period]
|
|
128
|
-
[429, {'Content-Type' => 'text/plain', 'Retry-After' => retry_after.to_s}, ["Retry later\n"]]
|
|
163
|
+
[429, { 'Content-Type' => 'text/plain', 'Retry-After' => retry_after.to_s }, ["Retry later\n"]]
|
|
129
164
|
}
|
|
130
165
|
|
|
131
166
|
def initialize(app)
|
|
@@ -134,23 +169,20 @@ class Rack::Attack
|
|
|
134
169
|
|
|
135
170
|
def call(env)
|
|
136
171
|
env['PATH_INFO'] = PathNormalizer.normalize_path(env['PATH_INFO'])
|
|
137
|
-
|
|
172
|
+
request = Rack::Attack::Request.new(env)
|
|
138
173
|
|
|
139
|
-
if safelisted?(
|
|
174
|
+
if safelisted?(request)
|
|
140
175
|
@app.call(env)
|
|
141
|
-
elsif blocklisted?(
|
|
176
|
+
elsif blocklisted?(request)
|
|
142
177
|
self.class.blocklisted_response.call(env)
|
|
143
|
-
elsif throttled?(
|
|
178
|
+
elsif throttled?(request)
|
|
144
179
|
self.class.throttled_response.call(env)
|
|
145
180
|
else
|
|
146
|
-
tracked?(
|
|
181
|
+
tracked?(request)
|
|
147
182
|
@app.call(env)
|
|
148
183
|
end
|
|
149
184
|
end
|
|
150
185
|
|
|
151
186
|
extend Forwardable
|
|
152
|
-
def_delegators self, :safelisted?,
|
|
153
|
-
:blocklisted?,
|
|
154
|
-
:throttled?,
|
|
155
|
-
:tracked?
|
|
187
|
+
def_delegators self, :safelisted?, :blocklisted?, :throttled?, :tracked?
|
|
156
188
|
end
|
|
@@ -3,11 +3,12 @@ module Rack
|
|
|
3
3
|
class Allow2Ban < Fail2Ban
|
|
4
4
|
class << self
|
|
5
5
|
protected
|
|
6
|
+
|
|
6
7
|
def key_prefix
|
|
7
8
|
'allow2ban'
|
|
8
9
|
end
|
|
9
10
|
|
|
10
|
-
# everything the same here except we
|
|
11
|
+
# everything is the same here except we only return true
|
|
11
12
|
# (blocking the request) if they have tripped the limit.
|
|
12
13
|
def fail!(discriminator, bantime, findtime, maxretry)
|
|
13
14
|
count = cache.count("#{key_prefix}:count:#{discriminator}", findtime)
|
data/lib/rack/attack/cache.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Rack
|
|
2
2
|
class Attack
|
|
3
3
|
class Cache
|
|
4
|
-
|
|
5
4
|
attr_accessor :prefix
|
|
5
|
+
attr_reader :last_epoch_time
|
|
6
6
|
|
|
7
7
|
def initialize
|
|
8
8
|
self.store = ::Rails.cache if defined?(::Rails.cache)
|
|
@@ -20,6 +20,9 @@ module Rack
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def read(unprefixed_key)
|
|
23
|
+
enforce_store_presence!
|
|
24
|
+
enforce_store_method_presence!(:read)
|
|
25
|
+
|
|
23
26
|
store.read("#{prefix}:#{unprefixed_key}")
|
|
24
27
|
end
|
|
25
28
|
|
|
@@ -39,22 +42,38 @@ module Rack
|
|
|
39
42
|
private
|
|
40
43
|
|
|
41
44
|
def key_and_expiry(unprefixed_key, period)
|
|
42
|
-
|
|
43
|
-
# Add 1 to expires_in to avoid timing error:
|
|
44
|
-
expires_in = (period - (
|
|
45
|
-
["#{prefix}:#{(
|
|
45
|
+
@last_epoch_time = Time.now.to_i
|
|
46
|
+
# Add 1 to expires_in to avoid timing error: https://git.io/i1PHXA
|
|
47
|
+
expires_in = (period - (@last_epoch_time % period) + 1).to_i
|
|
48
|
+
["#{prefix}:#{(@last_epoch_time / period).to_i}:#{unprefixed_key}", expires_in]
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def do_count(key, expires_in)
|
|
52
|
+
enforce_store_presence!
|
|
53
|
+
enforce_store_method_presence!(:increment)
|
|
54
|
+
|
|
49
55
|
result = store.increment(key, 1, :expires_in => expires_in)
|
|
50
56
|
|
|
51
57
|
# NB: Some stores return nil when incrementing uninitialized values
|
|
52
58
|
if result.nil?
|
|
59
|
+
enforce_store_method_presence!(:write)
|
|
60
|
+
|
|
53
61
|
store.write(key, 1, :expires_in => expires_in)
|
|
54
62
|
end
|
|
55
63
|
result || 1
|
|
56
64
|
end
|
|
57
65
|
|
|
66
|
+
def enforce_store_presence!
|
|
67
|
+
if store.nil?
|
|
68
|
+
raise Rack::Attack::MissingStoreError
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def enforce_store_method_presence!(method_name)
|
|
73
|
+
if !store.respond_to?(method_name)
|
|
74
|
+
raise Rack::Attack::MisconfiguredStoreError, "Configured store #{store.class.name} doesn't respond to ##{method_name} method"
|
|
75
|
+
end
|
|
76
|
+
end
|
|
58
77
|
end
|
|
59
78
|
end
|
|
60
79
|
end
|
data/lib/rack/attack/check.rb
CHANGED
|
@@ -7,17 +7,15 @@ module Rack
|
|
|
7
7
|
@type = options.fetch(:type, nil)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def
|
|
11
|
-
block
|
|
10
|
+
def matched_by?(request)
|
|
11
|
+
block.call(request).tap do |match|
|
|
12
12
|
if match
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Rack::Attack.instrument(
|
|
13
|
+
request.env["rack.attack.matched"] = name
|
|
14
|
+
request.env["rack.attack.match_type"] = type
|
|
15
|
+
Rack::Attack.instrument(request)
|
|
16
16
|
end
|
|
17
|
-
|
|
17
|
+
end
|
|
18
18
|
end
|
|
19
|
-
|
|
20
19
|
end
|
|
21
20
|
end
|
|
22
21
|
end
|
|
23
|
-
|
data/lib/rack/attack/fail2ban.rb
CHANGED
|
@@ -27,6 +27,7 @@ module Rack
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
protected
|
|
30
|
+
|
|
30
31
|
def key_prefix
|
|
31
32
|
'fail2ban'
|
|
32
33
|
end
|
|
@@ -40,8 +41,8 @@ module Rack
|
|
|
40
41
|
true
|
|
41
42
|
end
|
|
42
43
|
|
|
43
|
-
|
|
44
44
|
private
|
|
45
|
+
|
|
45
46
|
def ban!(discriminator, bantime)
|
|
46
47
|
cache.write("#{key_prefix}:ban:#{discriminator}", 1, bantime)
|
|
47
48
|
end
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
class Rack::Attack
|
|
2
|
-
|
|
3
2
|
# When using Rack::Attack with a Rails app, developers expect the request path
|
|
4
3
|
# to be normalized. In particular, trailing slashes are stripped.
|
|
5
|
-
# (See
|
|
4
|
+
# (See https://git.io/v0rrR for implementation.)
|
|
6
5
|
#
|
|
7
6
|
# Look for an ActionDispatch utility class that Rails folks would expect
|
|
8
7
|
# to normalize request paths. If unavailable, use a fallback class that
|
|
@@ -15,13 +14,9 @@ class Rack::Attack
|
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
PathNormalizer = if defined?(::ActionDispatch::Journey::Router::Utils)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
else
|
|
24
|
-
FallbackPathNormalizer
|
|
25
|
-
end
|
|
26
|
-
|
|
17
|
+
# For Rails apps
|
|
18
|
+
::ActionDispatch::Journey::Router::Utils
|
|
19
|
+
else
|
|
20
|
+
FallbackPathNormalizer
|
|
21
|
+
end
|
|
27
22
|
end
|
data/lib/rack/attack/safelist.rb
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
module Rack
|
|
2
2
|
class Attack
|
|
3
3
|
module StoreProxy
|
|
4
|
-
PROXIES = [DalliProxy, MemCacheProxy, RedisStoreProxy]
|
|
5
|
-
|
|
6
|
-
ACTIVE_SUPPORT_WRAPPER_CLASSES = Set.new(['ActiveSupport::Cache::MemCacheStore', 'ActiveSupport::Cache::RedisStore']).freeze
|
|
7
|
-
ACTIVE_SUPPORT_CLIENTS = Set.new(['Redis::Store', 'Dalli::Client', 'MemCache']).freeze
|
|
4
|
+
PROXIES = [DalliProxy, MemCacheStoreProxy, MemCacheProxy, RedisStoreProxy, RedisProxy, RedisCacheStoreProxy].freeze
|
|
8
5
|
|
|
9
6
|
def self.build(store)
|
|
10
7
|
client = unwrap_active_support_stores(store)
|
|
@@ -12,17 +9,11 @@ module Rack
|
|
|
12
9
|
klass ? klass.new(client) : client
|
|
13
10
|
end
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
private
|
|
17
12
|
def self.unwrap_active_support_stores(store)
|
|
18
13
|
# ActiveSupport::Cache::RedisStore doesn't expose any way to set an expiry,
|
|
19
14
|
# so use the raw Redis::Store instead.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
client = store.instance_variable_get(:@data)
|
|
24
|
-
if ACTIVE_SUPPORT_WRAPPER_CLASSES.include?(store.class.to_s) && ACTIVE_SUPPORT_CLIENTS.include?(client.class.to_s)
|
|
25
|
-
client
|
|
15
|
+
if store.class.name == 'ActiveSupport::Cache::RedisStore'
|
|
16
|
+
store.instance_variable_get(:@data)
|
|
26
17
|
else
|
|
27
18
|
store
|
|
28
19
|
end
|