diff --git a/config/initializers/rack/attack.rb b/config/initializers/rack/attack.rb index 1cc021eee..35a8a75a0 100644 --- a/config/initializers/rack/attack.rb +++ b/config/initializers/rack/attack.rb @@ -30,8 +30,8 @@ module Rack track_and_return_ip(request.env["HTTP_FASTLY_CLIENT_IP"]) end - throttle("message_throttle", limit: 2, period: 1) do |request| - if request.path.starts_with?("/messages") && request.post? + throttle("message_tag_throttle", limit: 2, period: 1) do |request| + if message_or_tag_request(request) track_and_return_ip(request.env["HTTP_FASTLY_CLIENT_IP"]) end end @@ -42,5 +42,10 @@ module Rack Honeycomb.add_field("fastly_client_ip", ip_address) ip_address.to_s end + + def self.message_or_tag_request(request) + (request.path.starts_with?("/messages") && request.post?) || + request.path.include?("/t/") + end end end diff --git a/spec/initializers/rack/attack_spec.rb b/spec/initializers/rack/attack_spec.rb index ff037d008..93c8a4f00 100644 --- a/spec/initializers/rack/attack_spec.rb +++ b/spec/initializers/rack/attack_spec.rb @@ -95,7 +95,7 @@ describe Rack::Attack, type: :request, throttle: true do end end - describe "message_throttle" do + describe "message_tag_throttle" do let(:user) { create(:user) } let(:chat_channel) { create(:chat_channel) } let(:new_message) do @@ -106,6 +106,8 @@ describe Rack::Attack, type: :request, throttle: true do chat_channel_id: chat_channel.id } end + let(:headers) { { "HTTP_FASTLY_CLIENT_IP" => "5.6.7.8" } } + let(:dif_headers) { { "HTTP_FASTLY_CLIENT_IP" => "1.1.1.1" } } before do allow(Pusher).to receive(:trigger).and_return(true) @@ -113,22 +115,46 @@ describe Rack::Attack, type: :request, throttle: true do end it "throttles creating messages" do - headers = { "HTTP_FASTLY_CLIENT_IP" => "5.6.7.8" } - dif_headers = { "HTTP_FASTLY_CLIENT_IP" => "1.1.1.1" } - Timecop.freeze do valid_responses = Array.new(2).map do post messages_path, params: { message: new_message }, headers: headers end throttled_response = post messages_path, params: { message: new_message }, headers: headers - new_api_response = post messages_path, params: { message: new_message }, headers: dif_headers + new_response = post messages_path, params: { message: new_message }, headers: dif_headers valid_responses.each { |r| expect(r).not_to eq(429) } expect(throttled_response).to eq(429) - expect(new_api_response).not_to eq(429) + expect(new_response).not_to eq(429) expect(Honeycomb).to have_received(:add_field).with("fastly_client_ip", "5.6.7.8").exactly(6).times expect(Honeycomb).to have_received(:add_field).with("fastly_client_ip", "1.1.1.1").exactly(2).times end end + + # rubocop:disable RSpec/AnyInstance, RSpec/ExampleLength + it "throttles viewing tags" do + allow_any_instance_of(StoriesController).to receive(:cached_tagged_count).and_return(0) + allow_any_instance_of(StoriesController).to receive(:stories_by_timeframe).and_return(Article.none) + allow_any_instance_of(Articles::Feeds::LargeForemExperimental).to receive( + :published_articles_by_tag, + ).and_return(Article.none) + tag_path = "/t/#{create(:tag).name}" + + get tag_path, headers: headers # warm up the slow endpoint + + Timecop.freeze do + valid_responses = Array.new(2).map do + get tag_path, headers: headers + end + throttled_response = get tag_path, headers: headers + new_response = get tag_path, headers: dif_headers + + expect(valid_responses.first).not_to eq(429) + expect(throttled_response).to eq(429) + expect(new_response).not_to eq(429) + expect(Honeycomb).to have_received(:add_field).with("fastly_client_ip", "5.6.7.8").exactly(8).times + expect(Honeycomb).to have_received(:add_field).with("fastly_client_ip", "1.1.1.1").exactly(2).times + end + end + # rubocop:enable RSpec/AnyInstance, RSpec/ExampleLength end end