Commit 1a4f4a00 authored by Quang-Minh Nguyen's avatar Quang-Minh Nguyen

Add RateLimit-Name header to indicate the name of throttle category

Issue https://gitlab.com/gitlab-org/gitlab/-/issues/296680
parent 00babc80
......@@ -13,7 +13,9 @@ module Gitlab
# This is Rack::Attack::DEFAULT_THROTTLED_RESPONSE, modified to allow a custom response
Rack::Attack.throttled_response = lambda do |env|
throttled_headers = Gitlab::RackAttack.throttled_response_headers(env['rack.attack.match_data'])
throttled_headers = Gitlab::RackAttack.throttled_response_headers(
env['rack.attack.matched'], env['rack.attack.match_data']
)
[429, { 'Content-Type' => 'text/plain' }.merge(throttled_headers), [Gitlab::Throttle.rate_limiting_response_text]]
end
......@@ -50,7 +52,7 @@ module Gitlab
# - RateLimit-Reset: Similar to Retry-After.
#
# - RateLimit-ResetTime: the point of time that the quest quota is reset.
def self.throttled_response_headers(match_data)
def self.throttled_response_headers(matched, match_data)
# Match data example:
# {:discriminator=>"127.0.0.1", :count=>12, :period=>60 seconds, :limit=>1, :epoch_time=>1609833930}
# Source: https://github.com/rack/rack-attack/blob/v6.3.0/lib/rack/attack/throttle.rb#L33
......@@ -62,6 +64,7 @@ module Gitlab
retry_after = period - (now % period)
reset_time = now + (period - now % period)
{
'RateLimit-Name' => matched.to_s,
'RateLimit-Limit' => rounded_limit.to_s,
'RateLimit-Observed' => observed.to_s,
'RateLimit-Remaining' => (limit > observed ? limit - observed : 0).to_s,
......
......@@ -100,9 +100,10 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
end
describe '.throttled_response_headers' do
where(:match_data, :headers) do
where(:matched, :match_data, :headers) do
[
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -111,6 +112,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 29, 30).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -120,6 +122,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -128,6 +131,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 59, 59).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -137,6 +141,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -145,6 +150,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 0, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -154,6 +160,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -162,6 +169,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 23, 30, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -171,6 +179,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -179,6 +188,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 30, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '57', # 56.66 requests per minute
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -188,6 +198,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -196,6 +207,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 30, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '62', # 61.66 requests per minute
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -205,6 +217,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -213,6 +226,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 30, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '1', # 0.9833 requests per minute
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -222,6 +236,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -230,6 +245,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 30, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '2', # 1.016 requests per minute
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -239,6 +255,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -247,6 +264,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 30, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '40',
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -256,6 +274,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
}
],
[
'throttle_unauthenticated',
{
discriminator: '127.0.0.1',
count: 3700,
......@@ -264,6 +283,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
epoch_time: Time.utc(2021, 1, 5, 10, 30, 0).to_i
},
{
'RateLimit-Name' => 'throttle_unauthenticated',
'RateLimit-Limit' => '23',
'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0',
......@@ -277,7 +297,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
with_them do
it 'generates accurate throttled headers' do
expect(described_class.throttled_response_headers(match_data)).to eql(headers)
expect(described_class.throttled_response_headers(matched, match_data)).to eql(headers)
end
end
end
......
......@@ -31,6 +31,10 @@ module RackAttackSpecHelpers
yield
expect(response).to have_gitlab_http_status(:too_many_requests)
expect(response).to have_header('RateLimit-Name')
expect(response.headers['RateLimit-Name']).to match(/^throttle_.*$/)
expect(response).to have_header('Retry-After')
expect(response.headers['Retry-After']).to match(/^\d+$/)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment