Commit 640c5e42 authored by Sean McGivern's avatar Sean McGivern

Merge branch...

Merge branch 'qmnguyen0711/795-ratelimit-reset-header-from-rackattack-differs-from-that-returned-by-haproxy' into 'master'

Convert RateLimit-Reset header format to Unix time

See merge request gitlab-org/gitlab!51680
parents d4ca3717 8e9be622
...@@ -46,15 +46,15 @@ blocked. The server may respond with rate-limiting information allowing the ...@@ -46,15 +46,15 @@ blocked. The server may respond with rate-limiting information allowing the
requester to retry after a specific period of time. These information are requester to retry after a specific period of time. These information are
attached into the response headers. attached into the response headers.
| Header | Example | Description | | Header | Example | Description |
|:----------------------|:--------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |:----------------------|:--------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `RateLimit-Limit` | `60` | The request quota for the client **each minute**. If the rate limit period set in the admin area is different from 1 minute, the value of this header is adjusted to approximately the nearest 60-minute period. | | `RateLimit-Limit` | `60` | The request quota for the client **each minute**. If the rate limit period set in the admin area is different from 1 minute, the value of this header is adjusted to approximately the nearest 60-minute period. |
| `RateLimit-Name` | `throttle_authenticated_web` | Name of the throttle blocking the requests. | | `RateLimit-Name` | `throttle_authenticated_web` | Name of the throttle blocking the requests. |
| `RateLimit-Observed` | `67` | Number of requests associated to the client in the time window. | | `RateLimit-Observed` | `67` | Number of requests associated to the client in the time window. |
| `RateLimit-Remaining` | `0` | Remaining quota in the time window. The result of `RateLimit-Limit` - `RateLimit-Remaining`. | | `RateLimit-Remaining` | `0` | Remaining quota in the time window. The result of `RateLimit-Limit` - `RateLimit-Remaining`. |
| `RateLimit-Reset` | `30` | An alias of `Retry-After` header. | | `RateLimit-Reset` | `1609844400` | [Unix time](https://en.wikipedia.org/wiki/Unix_time)-formatted time when the request quota is reset. |
| `RateLimit-ResetTime` | `Tue, 05 Jan 2021 11:00:00 GMT` | [RFC2616](https://tools.ietf.org/html/rfc2616#section-3.3.1)-formatted date and time when the request quota is reset. | | `RateLimit-ResetTime` | `Tue, 05 Jan 2021 11:00:00 GMT` | [RFC2616](https://tools.ietf.org/html/rfc2616#section-3.3.1)-formatted date and time when the request quota is reset. |
| `Retry-After` | `30` | Remaining duration **in seconds** until the quota is reset. This is a [standard HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After). | | `Retry-After` | `30` | Remaining duration **in seconds** until the quota is reset. This is a [standard HTTP header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After). |
## Use an HTTP header to bypass rate limiting ## Use an HTTP header to bypass rate limiting
......
...@@ -49,9 +49,9 @@ module Gitlab ...@@ -49,9 +49,9 @@ module Gitlab
# reset. This is a standardized HTTP header: # reset. This is a standardized HTTP header:
# https://tools.ietf.org/html/rfc7231#page-69 # https://tools.ietf.org/html/rfc7231#page-69
# #
# - RateLimit-Reset: Similar to Retry-After. # - RateLimit-Reset: the point of time that the request quota is reset, in Unix time
# #
# - RateLimit-ResetTime: the point of time that the quest quota is reset. # - RateLimit-ResetTime: the point of time that the request quota is reset, in HTTP date format
def self.throttled_response_headers(matched, match_data) def self.throttled_response_headers(matched, match_data)
# Match data example: # Match data example:
# {:discriminator=>"127.0.0.1", :count=>12, :period=>60 seconds, :limit=>1, :epoch_time=>1609833930} # {:discriminator=>"127.0.0.1", :count=>12, :period=>60 seconds, :limit=>1, :epoch_time=>1609833930}
...@@ -62,14 +62,14 @@ module Gitlab ...@@ -62,14 +62,14 @@ module Gitlab
observed = match_data[:count] observed = match_data[:count]
now = match_data[:epoch_time] now = match_data[:epoch_time]
retry_after = period - (now % period) retry_after = period - (now % period)
reset_time = now + (period - now % period) reset_time = Time.at(now + retry_after) # rubocop:disable Rails/TimeZone
{ {
'RateLimit-Name' => matched.to_s, 'RateLimit-Name' => matched.to_s,
'RateLimit-Limit' => rounded_limit.to_s, 'RateLimit-Limit' => rounded_limit.to_s,
'RateLimit-Observed' => observed.to_s, 'RateLimit-Observed' => observed.to_s,
'RateLimit-Remaining' => (limit > observed ? limit - observed : 0).to_s, 'RateLimit-Remaining' => (limit > observed ? limit - observed : 0).to_s,
'RateLimit-Reset' => retry_after.to_s, 'RateLimit-Reset' => reset_time.to_i.to_s,
'RateLimit-ResetTime' => Time.at(reset_time).httpdate, 'RateLimit-ResetTime' => reset_time.httpdate,
'Retry-After' => retry_after.to_s 'Retry-After' => retry_after.to_s
} }
end end
......
...@@ -116,7 +116,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -116,7 +116,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60', 'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '1830', 'RateLimit-Reset' => '1609844400', # Time.utc(2021, 1, 5, 11, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT',
'Retry-After' => '1830' 'Retry-After' => '1830'
} }
...@@ -135,7 +135,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -135,7 +135,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60', 'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '1', 'RateLimit-Reset' => '1609844400', # Time.utc(2021, 1, 5, 11, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT',
'Retry-After' => '1' 'Retry-After' => '1'
} }
...@@ -154,7 +154,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -154,7 +154,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60', 'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '3600', 'RateLimit-Reset' => '1609844400', # Time.utc(2021, 1, 5, 11, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT',
'Retry-After' => '3600' 'Retry-After' => '3600'
} }
...@@ -173,7 +173,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -173,7 +173,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60', 'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '1800', 'RateLimit-Reset' => '1609891200', # Time.utc(2021, 1, 6, 0, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Wed, 06 Jan 2021 00:00:00 GMT', # Next day 'RateLimit-ResetTime' => 'Wed, 06 Jan 2021 00:00:00 GMT', # Next day
'Retry-After' => '1800' 'Retry-After' => '1800'
} }
...@@ -192,7 +192,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -192,7 +192,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '57', # 56.66 requests per minute 'RateLimit-Limit' => '57', # 56.66 requests per minute
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '1800', 'RateLimit-Reset' => '1609844400', # Time.utc(2021, 1, 5, 11, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT',
'Retry-After' => '1800' 'Retry-After' => '1800'
} }
...@@ -211,7 +211,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -211,7 +211,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '62', # 61.66 requests per minute 'RateLimit-Limit' => '62', # 61.66 requests per minute
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '1800', 'RateLimit-Reset' => '1609844400', # Time.utc(2021, 1, 5, 11, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT',
'Retry-After' => '1800' 'Retry-After' => '1800'
} }
...@@ -230,7 +230,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -230,7 +230,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '1', # 0.9833 requests per minute 'RateLimit-Limit' => '1', # 0.9833 requests per minute
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '1800', 'RateLimit-Reset' => '1609844400', # Time.utc(2021, 1, 5, 11, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT',
'Retry-After' => '1800' 'Retry-After' => '1800'
} }
...@@ -249,7 +249,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -249,7 +249,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '2', # 1.016 requests per minute 'RateLimit-Limit' => '2', # 1.016 requests per minute
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '1800', 'RateLimit-Reset' => '1609844400', # Time.utc(2021, 1, 5, 11, 0, 0).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 11:00:00 GMT',
'Retry-After' => '1800' 'Retry-After' => '1800'
} }
...@@ -268,7 +268,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -268,7 +268,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '40', 'RateLimit-Limit' => '40',
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '15', 'RateLimit-Reset' => '1609842615', # Time.utc(2021, 1, 5, 10, 30, 15).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 10:30:15 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 10:30:15 GMT',
'Retry-After' => '15' 'Retry-After' => '15'
} }
...@@ -287,7 +287,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do ...@@ -287,7 +287,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '23', 'RateLimit-Limit' => '23',
'RateLimit-Observed' => '3700', 'RateLimit-Observed' => '3700',
'RateLimit-Remaining' => '0', 'RateLimit-Remaining' => '0',
'RateLimit-Reset' => '27', 'RateLimit-Reset' => '1609842627', # Time.utc(2021, 1, 5, 10, 30, 27).to_i.to_s
'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 10:30:27 GMT', 'RateLimit-ResetTime' => 'Tue, 05 Jan 2021 10:30:27 GMT',
'Retry-After' => '27' 'Retry-After' => '27'
} }
......
...@@ -36,9 +36,12 @@ module RackAttackSpecHelpers ...@@ -36,9 +36,12 @@ module RackAttackSpecHelpers
'RateLimit-Name' => a_string_matching(/^throttle_.*$/), 'RateLimit-Name' => a_string_matching(/^throttle_.*$/),
'RateLimit-Observed' => a_string_matching(/^\d+$/), 'RateLimit-Observed' => a_string_matching(/^\d+$/),
'RateLimit-Remaining' => a_string_matching(/^\d+$/), 'RateLimit-Remaining' => a_string_matching(/^\d+$/),
'RateLimit-Reset' => a_string_matching(/^\d+$/),
'Retry-After' => a_string_matching(/^\d+$/) 'Retry-After' => a_string_matching(/^\d+$/)
) )
expect(response).to have_header('RateLimit-Reset')
expect do
DateTime.strptime(response.headers['RateLimit-Reset'], '%s')
end.not_to raise_error
expect(response).to have_header('RateLimit-ResetTime') expect(response).to have_header('RateLimit-ResetTime')
expect do expect do
Time.httpdate(response.headers['RateLimit-ResetTime']) Time.httpdate(response.headers['RateLimit-ResetTime'])
......
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