mirror of
https://github.com/samsonjs/rack-attack.git
synced 2026-03-25 09:25:49 +00:00
Add a proxy to deal with ActiveSupport::Cache::MemCacheStore
If connection pooling is used with AS::Cache::MemCacheStore, unwrap_active_support_stores wouldn't return the underlying dalli instance(s), and so Rack::Attack.store would be the bare unproxied MemCacheStore instance. Calling write then increment would silently fail because :raw wasn't used. With this commit, we no longer try to unwrap AS::Cache::MemCacheStore instances.
This commit is contained in:
parent
03b8ce6f9e
commit
5cdc15b35a
5 changed files with 47 additions and 15 deletions
|
|
@ -19,6 +19,7 @@ class Rack::Attack
|
|||
autoload :StoreProxy, 'rack/attack/store_proxy'
|
||||
autoload :DalliProxy, 'rack/attack/store_proxy/dalli_proxy'
|
||||
autoload :MemCacheProxy, 'rack/attack/store_proxy/mem_cache_proxy'
|
||||
autoload :MemCacheStoreProxy, 'rack/attack/store_proxy/mem_cache_store_proxy'
|
||||
autoload :RedisProxy, 'rack/attack/store_proxy/redis_proxy'
|
||||
autoload :RedisStoreProxy, 'rack/attack/store_proxy/redis_store_proxy'
|
||||
autoload :RedisCacheStoreProxy, 'rack/attack/store_proxy/redis_cache_store_proxy'
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
module Rack
|
||||
class Attack
|
||||
module StoreProxy
|
||||
PROXIES = [DalliProxy, MemCacheProxy, RedisStoreProxy, RedisProxy, RedisCacheStoreProxy].freeze
|
||||
|
||||
ACTIVE_SUPPORT_WRAPPER_CLASSES = Set.new(['ActiveSupport::Cache::MemCacheStore', 'ActiveSupport::Cache::RedisStore', 'ActiveSupport::Cache::RedisCacheStore']).freeze
|
||||
ACTIVE_SUPPORT_CLIENTS = Set.new(['Redis::Store', 'Dalli::Client', 'MemCache']).freeze
|
||||
PROXIES = [DalliProxy, MemCacheStoreProxy, MemCacheProxy, RedisStoreProxy, RedisProxy, RedisCacheStoreProxy].freeze
|
||||
|
||||
def self.build(store)
|
||||
client = unwrap_active_support_stores(store)
|
||||
|
|
@ -17,15 +14,8 @@ module Rack
|
|||
def self.unwrap_active_support_stores(store)
|
||||
# ActiveSupport::Cache::RedisStore doesn't expose any way to set an expiry,
|
||||
# so use the raw Redis::Store instead.
|
||||
# We also want to use the underlying Dalli client instead of ::ActiveSupport::Cache::MemCacheStore,
|
||||
# and the MemCache client if using Rails 3.x
|
||||
|
||||
if store.instance_variable_defined?(:@data)
|
||||
client = store.instance_variable_get(:@data)
|
||||
end
|
||||
|
||||
if ACTIVE_SUPPORT_WRAPPER_CLASSES.include?(store.class.to_s) && ACTIVE_SUPPORT_CLIENTS.include?(client.class.to_s)
|
||||
client
|
||||
if store.class.name == 'ActiveSupport::Cache::RedisStore'
|
||||
store.instance_variable_get(:@data)
|
||||
else
|
||||
store
|
||||
end
|
||||
|
|
|
|||
19
lib/rack/attack/store_proxy/mem_cache_store_proxy.rb
Normal file
19
lib/rack/attack/store_proxy/mem_cache_store_proxy.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'delegate'
|
||||
|
||||
module Rack
|
||||
class Attack
|
||||
module StoreProxy
|
||||
class MemCacheStoreProxy < SimpleDelegator
|
||||
def self.handle?(store)
|
||||
defined?(::Dalli) && defined?(::ActiveSupport::Cache::MemCacheStore) && store.is_a?(::ActiveSupport::Cache::MemCacheStore)
|
||||
end
|
||||
|
||||
def write(name, value, options = {})
|
||||
super(name, value, options.merge!(raw: true))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative "../../spec_helper"
|
||||
|
||||
if defined?(::ConnectionPool) && defined?(::Dalli)
|
||||
require_relative "../../support/cache_store_helper"
|
||||
require "timecop"
|
||||
|
||||
describe "ActiveSupport::Cache::MemCacheStore (pooled) as a cache backend" do
|
||||
before do
|
||||
Rack::Attack.cache.store = ActiveSupport::Cache::MemCacheStore.new(pool_size: 2)
|
||||
end
|
||||
|
||||
after do
|
||||
Rack::Attack.cache.store.clear
|
||||
end
|
||||
|
||||
it_works_for_cache_backed_features(fetch_from_store: ->(key) {
|
||||
Rack::Attack.cache.store.read(key)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
@ -12,9 +12,9 @@ if defined?(::Dalli)
|
|||
end
|
||||
|
||||
after do
|
||||
Rack::Attack.cache.store.flush_all
|
||||
Rack::Attack.cache.store.clear
|
||||
end
|
||||
|
||||
it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.get(key) })
|
||||
it_works_for_cache_backed_features(fetch_from_store: ->(key) { Rack::Attack.cache.store.read(key) })
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue