lib/http/retriable/delay_calculator.rb
# frozen_string_literal: true module HTTP module Retriable # @api private class DelayCalculator def initialize(opts) @max_delay = opts.fetch(:max_delay, Float::MAX).to_f if (delay = opts[:delay]).respond_to?(:call) @delay_proc = opts.fetch(:delay) else @delay = delay end end def call(iteration, response) delay = if response && (retry_header = response.headers["Retry-After"]) delay_from_retry_header(retry_header) else calculate_delay_from_iteration(iteration) end ensure_dealy_in_bounds(delay) end RFC2822_DATE_REGEX = /^ (?:Sun|Mon|Tue|Wed|Thu|Fri|Sat),\s+ (?:0[1-9]|[1-2]?[0-9]|3[01])\s+ (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+ (?:19[0-9]{2}|[2-9][0-9]{3})\s+ (?:2[0-3]|[0-1][0-9]):(?:[0-5][0-9]):(?:60|[0-5][0-9])\s+ GMT $/x # Spec for Retry-After header # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After def delay_from_retry_header(value) value = value.to_s.strip case value when RFC2822_DATE_REGEX then DateTime.rfc2822(value).to_time - Time.now.utc when /^\d+$/ then value.to_i else 0 end end def calculate_delay_from_iteration(iteration) if @delay_proc @delay_proc.call(iteration) elsif @delay @delay else delay = (2**(iteration - 1)) - 1 delay_noise = rand delay + delay_noise end end def ensure_dealy_in_bounds(delay) delay.clamp(0, @max_delay) end end end end