class DuffelAPI::Middlewares::RateLimiter
Attributes
ratelimit_limit[RW]
ratelimit_remaining[RW]
ratelimit_reset[RW]
Public Class Methods
mutex()
click to toggle source
# File lib/duffel_api/middlewares/rate_limiter.rb, line 12 def mutex @mutex ||= Mutex.new end
new(app, options = {})
click to toggle source
Calls superclass method
# File lib/duffel_api/middlewares/rate_limiter.rb, line 17 def initialize(app, options = {}) super(app, options) end
Public Instance Methods
call(env)
click to toggle source
# File lib/duffel_api/middlewares/rate_limiter.rb, line 21 def call(env) sleep_until_ratelimit_reset if rate_limited? app.call(env).tap do |response| headers = response.env.response_headers RateLimiter.mutex.synchronize do RateLimiter.ratelimit_limit = new_ratelimit_limit(headers) RateLimiter.ratelimit_remaining = new_ratelimit_remaining(headers) RateLimiter.ratelimit_reset = new_ratelimit_reset(headers) end end end
Private Instance Methods
header_value_with_indifferent_key(headers, key)
click to toggle source
NOTE: The check for ‘ratelimit-limit’ vs ‘Ratelimit-Limit’ is required because RSpec and Rails proper handle HTTP headers differently. WebMock converts the header to the latter style, whereas when running through Rails it will be converted to the former.
# File lib/duffel_api/middlewares/rate_limiter.rb, line 41 def header_value_with_indifferent_key(headers, key) headers.find { |k, _v| k.casecmp(key).zero? }&.at(1) end
new_ratelimit_limit(headers)
click to toggle source
# File lib/duffel_api/middlewares/rate_limiter.rb, line 45 def new_ratelimit_limit(headers) header_value_with_indifferent_key(headers, "ratelimit-limit")&.to_i end
new_ratelimit_remaining(headers)
click to toggle source
# File lib/duffel_api/middlewares/rate_limiter.rb, line 49 def new_ratelimit_remaining(headers) header_value_with_indifferent_key(headers, "ratelimit-remaining")&.to_i end
new_ratelimit_reset(headers)
click to toggle source
# File lib/duffel_api/middlewares/rate_limiter.rb, line 53 def new_ratelimit_reset(headers) reset_time = header_value_with_indifferent_key(headers, "ratelimit-reset") reset_time.nil? ? nil : DateTime.parse(reset_time).to_time end
rate_limited?()
click to toggle source
# File lib/duffel_api/middlewares/rate_limiter.rb, line 58 def rate_limited? return false if RateLimiter.ratelimit_reset.nil? return false if RateLimiter.ratelimit_remaining.nil? RateLimiter.ratelimit_remaining.zero? end
sleep_until_ratelimit_reset()
click to toggle source
# File lib/duffel_api/middlewares/rate_limiter.rb, line 65 def sleep_until_ratelimit_reset sleep_time = (RateLimiter.ratelimit_reset.to_i - Time.now.to_i) + 1 return unless sleep_time.positive? ::Logger.new($stdout).info( "Duffel rate-limit hit. Sleeping for #{sleep_time} seconds", ) Kernel.sleep(sleep_time) end