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
requester to retry after a specific period of time. These information are
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-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-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-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). |
| `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-Remaining` | `0` | Remaining quota in the time window. The result of `RateLimit-Limit` - `RateLimit-Remaining`. |
| `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. |
| `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
......
......@@ -49,9 +49,9 @@ module Gitlab
# reset. This is a standardized HTTP header:
# 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)
# Match data example:
# {:discriminator=>"127.0.0.1", :count=>12, :period=>60 seconds, :limit=>1, :epoch_time=>1609833930}
......@@ -62,14 +62,14 @@ module Gitlab
observed = match_data[:count]
now = match_data[:epoch_time]
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-Limit' => rounded_limit.to_s,
'RateLimit-Observed' => observed.to_s,
'RateLimit-Remaining' => (limit > observed ? limit - observed : 0).to_s,
'RateLimit-Reset' => retry_after.to_s,
'RateLimit-ResetTime' => Time.at(reset_time).httpdate,
'RateLimit-Reset' => reset_time.to_i.to_s,
'RateLimit-ResetTime' => reset_time.httpdate,
'Retry-After' => retry_after.to_s
}
end
......
......@@ -116,7 +116,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '1830'
}
......@@ -135,7 +135,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '1'
}
......@@ -154,7 +154,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '3600'
}
......@@ -173,7 +173,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '60',
'RateLimit-Observed' => '3700',
'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
'Retry-After' => '1800'
}
......@@ -192,7 +192,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '57', # 56.66 requests per minute
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '1800'
}
......@@ -211,7 +211,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '62', # 61.66 requests per minute
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '1800'
}
......@@ -230,7 +230,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '1', # 0.9833 requests per minute
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '1800'
}
......@@ -249,7 +249,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '2', # 1.016 requests per minute
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '1800'
}
......@@ -268,7 +268,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '40',
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '15'
}
......@@ -287,7 +287,7 @@ RSpec.describe Gitlab::RackAttack, :aggregate_failures do
'RateLimit-Limit' => '23',
'RateLimit-Observed' => '3700',
'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',
'Retry-After' => '27'
}
......
......@@ -36,9 +36,12 @@ module RackAttackSpecHelpers
'RateLimit-Name' => a_string_matching(/^throttle_.*$/),
'RateLimit-Observed' => a_string_matching(/^\d+$/),
'RateLimit-Remaining' => a_string_matching(/^\d+$/),
'RateLimit-Reset' => 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 do
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