dryruby / rack-throttle

Rack middleware for rate-limiting incoming HTTP requests.

Home Page:http://rubygems.org/gems/rack-throttle

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ActiveSupport::Cache::MemCacheStore unsupported

owentran opened this issue · comments

Hi.

We recently upgraded from the dalli gem to mem_cache_store. This silently broke the rack-throttle gem since it expects the cache to support :has_key?, :get, and :set.

To fix it, we applied this monkey patch...

  class RackThrottleMemCacheStore < ActiveSupport::Cache::MemCacheStore
    alias_method :has_key?, :exist?
    alias_method :get, :read
    alias_method :set, :write
  end

  # Limits :max requests per minute by session id
  cache_servers   = Rails.application.config.cache_store[1...-1]
  cache_options   = Rails.application.config.cache_store.last
  memcache_store  = RackThrottleMemCacheStore.new(cache_servers, cache_options)

  Rails.application.config.middleware.use Rack::Throttle::Minute,
                                          max:        60,
                                          key_prefix: :throttle,
                                          cache:      memcache_store

I'm not sure if there's a better way but sharing this tidbit for others who may be running this with dalli and then switched to mem_cache_store.

Thanks for this gem!
Owen

Hi Owen! Most people are using rack-throttle for legacy versions of ruby, but if you are able to: https://github.com/kickstarter/rack-attack is much more feature full & maintained!

Thanks FreekingDean. I'll work on migrating with our next sprint and add any gotchas/comments to this issue.

Would love to know if there are large sweeping differences, but I believe it was originally based on this repo.

The switch took about an hour.

  • safelist took care of the IP whitelisting
  • Created a Rack::Attack::Request like the example config to whitelist localhost IP and a few paths
  • Able to create three different throttles based on different paths
  • Plugs right into Rails.cache.store without any issues

The hardest part was understanding how to log if something was throttled. Here was my solution...

ActiveSupport::Notifications.subscribe("throttle.rack_attack") do |_name, _start, _finish, _request_id, payload|
  request = payload[:request]
  Rails.logger.error("[rack-attack] throttle=#{request.env["rack.attack.matched"]}, path=#{request.path}, ip=#{request.ip}")
end

Will throw it into production in two weeks and see how it performs.

Works great. Only issue was that the IP method needs to be overwritten if you deal with HTTP_X_FORWARDED_FOR otherwise you'll block all traffic from your load balancers.

  def ip
    if addr = env['HTTP_X_FORWARDED_FOR']
      (addr.split(',').first || env['REMOTE_ADDR']).to_s.strip
    else
      env['REMOTE_ADDR']
    end
  end