Class: Faraday::Retry::Middleware

Inherits:
Middleware
  • Object
show all
Defined in:
lib/faraday/retry/middleware.rb

Overview

This class provides the main implementation for your middleware. Your middleware can implement any of the following methods:

  • on_request - called when the request is being prepared

  • on_complete - called when the response is being processed

Optionally, you can also override the following methods from Faraday::Middleware

  • initialize(app, options = {}) - the initializer method

  • call(env) - the main middleware invocation method. This already calls on_request and on_complete, so you normally don’t need to override it. You may need to in case you need to “wrap” the request or need more control (see “retry” middleware: github.com/lostisland/faraday/blob/main/lib/faraday/request/retry.rb#L142). IMPORTANT: Remember to call ‘@app.call(env)` or `super` to not interrupt the middleware chain!

Defined Under Namespace

Classes: Options

Constant Summary collapse

DEFAULT_EXCEPTIONS =
[
  Errno::ETIMEDOUT, 'Timeout::Error',
  Faraday::TimeoutError, Faraday::RetriableResponse
].freeze
IDEMPOTENT_METHODS =
%i[delete get head options put].freeze

Instance Method Summary collapse

Constructor Details

#initialize(app, options = nil) ⇒ Middleware

Returns a new instance of Middleware.

Parameters:

  • app (#call)
  • options (Hash) (defaults to: nil)

Options Hash (options):

  • :max (Integer) — default: 2

    Maximum number of retries

  • :interval (Integer) — default: 0

    Pause in seconds between retries

  • :interval_randomness (Integer) — default: 0

    The maximum random interval amount expressed as a float between 0 and 1 to use in addition to the interval.

  • :max_interval (Integer) — default: Float::MAX

    An upper limit for the interval

  • :backoff_factor (Integer) — default: 1

    The amount to multiply each successive retry’s interval amount by in order to provide backoff

  • :exceptions (Array) — default: [ Errno::ETIMEDOUT, 'Timeout::Error', Faraday::TimeoutError, Faraday::RetriableResponse]

    The list of exceptions to handle. Exceptions can be given as Class, Module, or String.

  • :methods (Array) — default: the idempotent HTTP methods in IDEMPOTENT_METHODS

    A list of HTTP methods to retry without calling retry_if. Pass an empty Array to call retry_if for all exceptions.

  • :retry_if (Block) — default: false

    block that will receive the env object and the exception raised and should decide if the code should retry still the action or not independent of the retry count. This would be useful if the exception produced is non-recoverable or if the the HTTP method called is not idempotent.

  • :retry_block (Block)

    block that is executed before every retry. Request environment, middleware options, current number of retries and the exception is passed to the block as parameters.

  • :retry_statuses (Array)

    Array of Integer HTTP status codes or a single Integer value that determines whether to raise a Faraday::RetriableResponse exception based on the HTTP status code of an HTTP response.



114
115
116
117
118
# File 'lib/faraday/retry/middleware.rb', line 114

def initialize(app, options = nil)
  super(app)
  @options = Options.from(options)
  @errmatch = build_exception_matcher(@options.exceptions)
end

Instance Method Details

#build_exception_matcher(exceptions) ⇒ Module

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

An exception matcher for the rescue clause can usually be any object that responds to ‘===`, but for Ruby 1.8 it has to be a Class or Module.

Parameters:

  • exceptions (Array)

Returns:

  • (Module)

    an exception matcher



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/faraday/retry/middleware.rb', line 166

def build_exception_matcher(exceptions)
  matcher = Module.new
  (
    class << matcher
      self
    end).class_eval do
    define_method(:===) do |error|
      exceptions.any? do |ex|
        if ex.is_a? Module
          error.is_a? ex
        else
          Object.const_defined?(ex.to_s) && error.is_a?(Object.const_get(ex.to_s))
        end
      end
    end
  end
  matcher
end

#calculate_sleep_amount(retries, env) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/faraday/retry/middleware.rb', line 120

def calculate_sleep_amount(retries, env)
  retry_after = calculate_retry_after(env)
  retry_interval = calculate_retry_interval(retries)

  return if retry_after && retry_after > @options.max_interval

  if retry_after && retry_after >= retry_interval
    retry_after
  else
    retry_interval
  end
end

#call(env) ⇒ Object

Parameters:

  • env (Faraday::Env)


134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/faraday/retry/middleware.rb', line 134

def call(env)
  retries = @options.max
  request_body = env[:body]
  begin
    # after failure env[:body] is set to the response body
    env[:body] = request_body
    @app.call(env).tap do |resp|
      raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
    end
  rescue @errmatch => e
    if retries.positive? && retry_request?(env, e)
      retries -= 1
      rewind_files(request_body)
      if (sleep_amount = calculate_sleep_amount(retries + 1, env))
        @options.retry_block.call(env, @options, retries, e)
        sleep sleep_amount
        retry
      end
    end

    raise unless e.is_a?(Faraday::RetriableResponse)

    e.response
  end
end