Class: Gitlab::RackAttack::RequestThrottleData
- Inherits:
-
Object
- Object
- Gitlab::RackAttack::RequestThrottleData
- Defined in:
- lib/gitlab/rack_attack/request_throttle_data.rb
Overview
Represents throttle information for a request, typically populated from Rack::Attack data.
This class encapsulates rate limit state for a specific throttle, including the limit, current observation count, and time window. It provides methods to calculate remaining quota and generate standardized rate limit HTTP headers.
Instance Attribute Summary collapse
-
#limit ⇒ Object
readonly
Returns the value of attribute limit.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#now ⇒ Object
readonly
Returns the value of attribute now.
-
#observed ⇒ Object
readonly
Returns the value of attribute observed.
-
#period ⇒ Object
readonly
Returns the value of attribute period.
Class Method Summary collapse
-
.from_rack_attack(name, data) ⇒ RequestThrottleData?
Creates a RequestThrottleData instance from Rack::Attack throttle data.
Instance Method Summary collapse
-
#common_response_headers ⇒ Hash<String, String>
Returns common rate limit headers for all requests (both throttled and unthrottled).
-
#initialize(name:, period:, limit:, observed:, now:) ⇒ RequestThrottleData
constructor
Initialize a new RequestThrottleData instance.
-
#remaining ⇒ Integer
Calculates the remaining request quota in the current time window.
-
#reset_time ⇒ Time
Calculates the time when the rate limit window will reset.
-
#retry_after ⇒ Integer
Calculates seconds remaining until the rate limit window resets.
-
#rounded_limit ⇒ Integer
Calculates the request limit normalized to a 60-second window.
-
#throttled_response_headers ⇒ Hash<String, String>
Returns rate limit headers for throttled requests (HTTP 429 responses).
Constructor Details
#initialize(name:, period:, limit:, observed:, now:) ⇒ RequestThrottleData
Initialize a new RequestThrottleData instance
75 76 77 78 79 80 81 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 75 def initialize(name:, period:, limit:, observed:, now:) @name = name @period = period @limit = limit @observed = observed @now = now end |
Instance Attribute Details
#limit ⇒ Object (readonly)
Returns the value of attribute limit.
25 26 27 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 25 def limit @limit end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
25 26 27 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 25 def name @name end |
#now ⇒ Object (readonly)
Returns the value of attribute now.
25 26 27 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 25 def now @now end |
#observed ⇒ Object (readonly)
Returns the value of attribute observed.
25 26 27 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 25 def observed @observed end |
#period ⇒ Object (readonly)
Returns the value of attribute period.
25 26 27 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 25 def period @period end |
Class Method Details
.from_rack_attack(name, data) ⇒ RequestThrottleData?
Creates a RequestThrottleData instance from Rack::Attack throttle data
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 36 def self.from_rack_attack(name, 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 unless name Gitlab::AppLogger.warn( class: self.name.to_s, message: '.from_rack_attack called with nil throttle name' ) return end required_keys = %i[count epoch_time period limit] missing_keys = required_keys.reject { |key| data.key?(key) } if missing_keys.any? Gitlab::AppLogger.warn( class: self.name.to_s, message: ".from_rack_attack called with incomplete data for throttle #{name} (#{missing_keys.join(', ')}" ) return end new( name: name.to_s, observed: data[:count].to_i, now: data[:epoch_time].to_i, period: data[:period].to_i, limit: data[:limit].to_i ) end |
Instance Method Details
#common_response_headers ⇒ Hash<String, String>
Returns common rate limit headers for all requests (both throttled and unthrottled)
These headers follow the IETF draft specification for rate limit headers. The limit is normalized (and approximately rounded up) to a 60-second window for compatibility with HAProxy and ecosystem libraries that expect this convention.
98 99 100 101 102 103 104 105 106 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 98 def common_response_headers { 'RateLimit-Name' => name.to_s, 'RateLimit-Limit' => rounded_limit.to_s, 'RateLimit-Observed' => observed.to_i.to_s, 'RateLimit-Remaining' => remaining.to_i.to_s, 'RateLimit-Reset' => reset_time.to_i.to_s } end |
#remaining ⇒ Integer
Calculates the remaining request quota in the current time window
145 146 147 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 145 def remaining (limit > observed ? limit - observed : 0) end |
#reset_time ⇒ Time
Calculates the time when the rate limit window will reset
159 160 161 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 159 def reset_time Time.at(now + retry_after) # rubocop:disable Rails/TimeZone -- Unix epoch based calculation end |
#retry_after ⇒ Integer
Calculates seconds remaining until the rate limit window resets
152 153 154 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 152 def retry_after period - (now % period) end |
#rounded_limit ⇒ Integer
Calculates the request limit normalized to a 60-second window
Since HAProxy and many ecosystem libraries expect rate limits expressed as requests per 60 seconds, this method converts the actual limit to that convention.
138 139 140 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 138 def rounded_limit (limit.to_f * 1.minute / period).ceil end |
#throttled_response_headers ⇒ Hash<String, String>
Returns rate limit headers for throttled requests (HTTP 429 responses)
Includes all headers from #common_response_headers plus additional headers to indicate when the client can retry.
119 120 121 122 123 124 125 126 |
# File 'lib/gitlab/rack_attack/request_throttle_data.rb', line 119 def throttled_response_headers common_response_headers.merge( { 'Retry-After' => retry_after.to_s, 'RateLimit-ResetTime' => reset_time.httpdate } ) end |