This is my repo
I just added the rack-attack gem.
gem 'rack-attack'
And this is my app/initializers/rack-attack.rb file:
class Rack::Attack
Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new
whitelist('allow-localhost') do |req|
'127.0.0.1' == req.ip || '::1' == req.ip
end
throttle('req/ip', limit: 10, period: 10) do |req|
req.ip
end
self.throttled_response = ->(env) {
retry_after = (env['rack.attack.match_data'] || {})[:period]
[
429,
{'Content-Type' => 'application/json', 'Retry-After' => retry_after.to_s},
[{error: "Throttle limit reached. Retry later."}.to_json]
]
}
end
This is my application.rb file:
module ApiCodeship
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Only loads a smaller set of middleware suitable for API only apps.
# Middleware like session, flash, cookies can be added back manually.
# Skip views, helpers and assets when generating a new resource.
config.api_only = true
config.middleware.use Rack::Attack
end
end
When I visit http://localhost:3000/rental_units, this is my logs in my console:
Started GET "/rental_units" for ::1 at 2016-03-03 23:01:32 -0500
ActiveRecord::SchemaMigration Load (0.4ms) SELECT "schema_migrations".* FROM "schema_migrations"
Processing by RentalUnitsController#index as HTML
RentalUnit Load (0.5ms) SELECT "rental_units".* FROM "rental_units"
[active_model_serializers] Dalli::Server#connect localhost:11211
[active_model_serializers] User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
[active_model_serializers] User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModel::Serializer::Adapter::JsonApi (44.37ms)
Completed 200 OK in 62ms (Views: 57.8ms | ActiveRecord: 2.7ms)
How do I know I'm throttling correctly?
I have recently been implementing rack attack inside my application. I found a couple of really helpful blog posts about testing Rack::Attack.
Essentially the below advise that you install the gem 'rack-test'
You can then include Rack::Test::Methods
at the top of an rspec file which will enable you to write tests such as;
describe 'throttling urls' do
include Rack::Test::Methods
def app
Rails.application
end
describe 'throttle excessive requests by IP address' do
let(:limit) { 10 }
context 'number of requests is lower than the limit' do
it "does not chnage the request status" do
limit.times do
get '/show', {}, "REMOTE_ADDR" => "1.2.3.4"
expect(last_response.status).to_not eq 429
end
end
end
context 'number of requests is higher than the limit' do
it 'changes the request status to 429' do
(limit * 2).times do |i|
get '/show', {}, "REMOTE_ADDR" => "1.2.3.5"
expect(last_response.status).to eq(429) if i > limit
end
end
end
end
end
The blogs i followed were;
Great blog post if using the old rspec syntax
More recent blog about testing rack attack but slightly less detailed
Probably development rails server should start caching. In file development.rb change:
config.action_controller.perform_caching = false
to
config.action_controller.perform_caching = true
And restart server. After 10 + 1 requests to your throttled route you will receive expected block/throttle.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With