faraday-retry 1.0.3 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2605913141029cfe8448ca6b6007a1c892a7ce8629689c0b979035ad59a6e975
4
- data.tar.gz: ceb3abfc225c30b4ecc0868be11bbb52e88083e4edf149eb8e40f2be3a87d70b
3
+ metadata.gz: 4215b6f0b7dd61fa8702103e77d6373cace2c46b469defeb0ed93ce94aa26e6c
4
+ data.tar.gz: 295faca61565e49a36a7f03201d6aee90b6a353c40d22a8bf8e1f6b1c729c041
5
5
  SHA512:
6
- metadata.gz: 21bfc830b3c1fd43f16b9dcb1b44a682cdb0fd3fd9c7cbc708cd998f676d7bcdbab5beea1d7f7905ba5cb6d631ba4e5dd2347be8ae7c73def1baf8bbe45f9b8e
7
- data.tar.gz: 7dea5532d1c3045da07ad24dd76157083cecbbfa96be0fea7b07f45be6114be0e18f191b875d8e76349c7dd5bad3965de073a8ddc1f20b83b18ab1dfe7d2a133
6
+ metadata.gz: 5b8f2bee0e35492efc578659ce28a0b947374c8cafdba07606d953c417e8844128248a639392e4ad60c682528c0d478d3a894ca688e1f72965172633aee3be7f
7
+ data.tar.gz: 148ac2094f76cd59ea9278a4d1948b3211f0c2abc1876b6f10fbdd88a384257dfdfab33f9a1cf102ed23352b5b0dd2e9f1f4d533cd8459c4b86d1f5e7578a053
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ * Avoid deprecation warning about ::UploadIO constant when used without faraday-multipart gem [PR #37](https://github.com/lostisland/faraday-retry/pull/37) [@iMacTia]
6
+ * Documentation update [PR #30](https://github.com/lostisland/faraday-retry/pull/30) [@olleolleolle]
7
+ * Documentation update [PR #32](https://github.com/lostisland/faraday-retry/pull/32) Thanks, [@Drowze]!
8
+
9
+ ## v2.2.0 (2023-06-01)
10
+
11
+ * Support new `header_parser_block` option. [PR #28](https://github.com/lostisland/faraday-retry/pull/28). Thanks, [@zavan]!
12
+
13
+ ## v2.1.0 (2023-03-03)
14
+
15
+ * Support for custom RateLimit headers. [PR #13](https://github.com/lostisland/faraday-retry/pull/13). Thanks, [@brookemckim]!
16
+
17
+ v2.1.1 (2023-02-17) is a spurious not-released version that you may have seen mentioned in this CHANGELOG.
18
+
19
+ ## v2.0.0 (2022-06-08)
20
+
21
+ ### Changed
22
+
23
+ * `retry_block` now takes keyword arguments instead of positional (backwards incompatible)
24
+ * `retry_block`'s `retry_count` argument now counts up from 0, instead of old `retries_remaining`
25
+
26
+ ### Added
27
+
28
+ * Support for the `RateLimit-Reset` header. [PR #9](https://github.com/lostisland/faraday-retry/pull/9). Thanks, [@maxprokopiev]!
29
+ * `retry_block` has additional `will_retry_in` argument with upcoming delay before retry in seconds.
30
+
3
31
  ## v1.0
4
32
 
5
33
  Initial release.
@@ -8,3 +36,10 @@ This release consists of the same middleware that was previously bundled with Fa
8
36
  ### Fixed
9
37
 
10
38
  * Retry middleware `retry_block` is not called if retry will not happen due to `max_interval`, https://github.com/lostisland/faraday/pull/1350
39
+
40
+ [@maxprokopiev]: https://github.com/maxprokopiev
41
+ [@brookemckim]: https://github.com/brookemckim
42
+ [@zavan]: https://github.com/zavan
43
+ [@Drowze]: https://github.com/Drowze
44
+ [@olleolleolle]: https://github.com/olleolleolle
45
+ [@iMacTia]: https://github.com/iMacTia
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Faraday Retry
2
2
 
3
- [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/lostisland/faraday-retry/CI)](https://github.com/lostisland/faraday-retry/actions?query=branch%3Amain)
3
+ [![CI](https://github.com/lostisland/faraday-retry/actions/workflows/ci.yaml/badge.svg)](https://github.com/lostisland/faraday-retry/actions/workflows/ci.yaml)
4
4
  [![Gem](https://img.shields.io/gem/v/faraday-retry.svg?style=flat-square)](https://rubygems.org/gems/faraday-retry)
5
5
  [![License](https://img.shields.io/github/license/lostisland/faraday-retry.svg?style=flat-square)](LICENSE.md)
6
6
 
@@ -75,7 +75,7 @@ retry_options = {
75
75
  #### Specify which exceptions should trigger a retry
76
76
 
77
77
  You can provide an `exceptions` option with a list of exceptions that will replace
78
- the default list of network-related exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`.
78
+ the default exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`, `Faraday::Error::RetriableResponse`.
79
79
  This can be particularly useful when combined with the [RaiseError][raise_error] middleware.
80
80
 
81
81
  ```ruby
@@ -84,6 +84,14 @@ retry_options = {
84
84
  }
85
85
  ```
86
86
 
87
+ If you want to inherit default exceptions, do it this way.
88
+
89
+ ```ruby
90
+ retry_options = {
91
+ exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Faraday::ResourceNotFound, Faraday::UnauthorizedError]
92
+ }
93
+ ```
94
+
87
95
  #### Specify on which response statuses to retry
88
96
 
89
97
  By default the `Retry` middleware will only retry the request if one of the expected exceptions arise.
@@ -96,11 +104,14 @@ retry_options = {
96
104
  }
97
105
  ```
98
106
 
99
- #### Automatically handle the `Retry-After` header
107
+ #### Automatically handle the `Retry-After` and `RateLimit-Reset` headers
100
108
 
101
- Some APIs, like the [Slack API](https://api.slack.com/docs/rate-limits), will inform you when you reach their API limits by replying with a response status code of `429` and a response header of `Retry-After` containing a time in seconds. You should then only retry querying after the amount of time provided by the `Retry-After` header, otherwise you won't get a response.
109
+ Some APIs, like the [Slack API](https://api.slack.com/docs/rate-limits), will inform you when you reach their API limits by replying with a response status code of `429`
110
+ and a response header of `Retry-After` containing a time in seconds. You should then only retry querying after the amount of time provided by the `Retry-After` header,
111
+ otherwise you won't get a response. Other APIs communicate their rate limits via the [RateLimit-xxx](https://www.ietf.org/archive/id/draft-ietf-httpapi-ratelimit-headers-05.html#name-providing-ratelimit-fields) headers
112
+ where `RateLimit-Reset` behaves similarly to the `Retry-After`.
102
113
 
103
- You can automatically handle this and have Faraday pause and retry for the right amount of time by including the `429` status code in the retry statuses list:
114
+ You can automatically handle both headers and have Faraday pause and retry for the right amount of time by including the `429` status code in the retry statuses list:
104
115
 
105
116
  ```ruby
106
117
  retry_options = {
@@ -108,6 +119,17 @@ retry_options = {
108
119
  }
109
120
  ```
110
121
 
122
+ If you are working with an API which does not comply with the Rate Limit RFC you can specify custom headers to be used for retry and reset, as well as a block to parse the headers:
123
+
124
+ ```ruby
125
+ retry_options = {
126
+ retry_statuses: [429],
127
+ rate_limit_retry_header: 'x-rate-limit-retry-after',
128
+ rate_limit_reset_header: 'x-rate-limit-reset',
129
+ header_parser_block: ->(value) { Time.at(value.to_i).utc - Time.now.utc }
130
+ }
131
+ ```
132
+
111
133
  #### Specify a custom retry logic
112
134
 
113
135
  You can also specify a custom retry logic with the `retry_if` option.
@@ -127,15 +149,17 @@ retry_options = {
127
149
 
128
150
  ### Call a block on every retry
129
151
 
130
- You can specify a block through the `retry_block` option that will be called before every retry.
131
- There are many different applications for this feature, spacing from instrumentation to monitoring.
132
- Request environment, middleware options, current number of retries and the exception is passed to the block as parameters.
152
+ You can specify a proc object through the `retry_block` option that will be called before every
153
+ retry, before There are many different applications for this feature, spacing from instrumentation to monitoring.
154
+
155
+ The block is passed keyword arguments with contextual information: Request environment, middleware options, current number of retries, exception, and amount of time we will wait before retrying. (retry_block is called before the wait time happens)
156
+
133
157
  For example, you might want to keep track of the response statuses:
134
158
 
135
159
  ```ruby
136
160
  response_statuses = []
137
161
  retry_options = {
138
- retry_block: -> (env, options, retries, exc) { response_statuses << env.status }
162
+ retry_block: -> (env:, options:, retry_count:, exception:, will_retry_in:) { response_statuses << env.status }
139
163
  }
140
164
  ```
141
165
 
@@ -147,8 +171,9 @@ Then, run `bin/test` to run the tests.
147
171
 
148
172
  To install this gem onto your local machine, run `rake build`.
149
173
 
150
- To release a new version, make a commit with a message such as "Bumped to 0.0.2" and then run `rake release`.
151
- See how it works [here](https://bundler.io/guides/creating_gem.html#releasing-the-gem).
174
+ ### Releasing a new version
175
+
176
+ To release a new version, make a commit with a message such as "Bumped to 0.0.2", and change the _Unreleased_ heading in `CHANGELOG.md` to a heading like "0.0.2 (2022-01-01)", and then use GitHub Releases to author a release. A GitHub Actions workflow then publishes a new gem to [RubyGems.org](https://rubygems.org/gems/faraday-retry).
152
177
 
153
178
  ## Contributing
154
179
 
@@ -158,4 +183,4 @@ Bug reports and pull requests are welcome on [GitHub](https://github.com/lostisl
158
183
 
159
184
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
160
185
 
161
- [raise_error]: https://lostisland.github.io/faraday/middleware/raise-error
186
+ [raise_error]: https://lostisland.github.io/faraday/#/middleware/included/raising-errors
@@ -26,7 +26,8 @@ module Faraday
26
26
  :interval_randomness,
27
27
  :backoff_factor, :exceptions,
28
28
  :methods, :retry_if, :retry_block,
29
- :retry_statuses)
29
+ :retry_statuses, :rate_limit_retry_header,
30
+ :rate_limit_reset_header, :header_parser_block)
30
31
 
31
32
  DEFAULT_CHECK = ->(_env, _exception) { false }
32
33
 
@@ -71,7 +72,7 @@ module Faraday
71
72
  end
72
73
 
73
74
  def retry_block
74
- self[:retry_block] ||= proc {} # rubocop:disable Lint/EmptyBlock
75
+ self[:retry_block] ||= proc {}
75
76
  end
76
77
 
77
78
  def retry_statuses
@@ -94,8 +95,8 @@ module Faraday
94
95
  # 'Timeout::Error', Faraday::TimeoutError, Faraday::RetriableResponse])
95
96
  # The list of exceptions to handle. Exceptions can be given as
96
97
  # Class, Module, or String.
97
- # @option options [Array] :methods (the idempotent HTTP methods
98
- # in IDEMPOTENT_METHODS) A list of HTTP methods to retry without
98
+ # @option options [Array<Symbol>] :methods (the idempotent HTTP methods
99
+ # in IDEMPOTENT_METHODS) A list of HTTP methods, as symbols, to retry without
99
100
  # calling retry_if. Pass an empty Array to call retry_if
100
101
  # for all exceptions.
101
102
  # @option options [Block] :retry_if (false) block that will receive
@@ -105,12 +106,24 @@ module Faraday
105
106
  # if the exception produced is non-recoverable or if the
106
107
  # the HTTP method called is not idempotent.
107
108
  # @option options [Block] :retry_block block that is executed before
108
- # every retry. Request environment, middleware options, current number
109
- # of retries and the exception is passed to the block as parameters.
109
+ # every retry. The block will be yielded keyword arguments:
110
+ # * env [Faraday::Env]: Request environment
111
+ # * options [Faraday::Options]: middleware options
112
+ # * retry_count [Integer]: how many retries have already occured (starts at 0)
113
+ # * exception [Exception]: exception that triggered the retry,
114
+ # will be the synthetic `Faraday::RetriableResponse` if the
115
+ # retry was triggered by something other than an exception.
116
+ # * will_retry_in [Float]: retry_block is called *before* the retry
117
+ # delay, actual retry will happen in will_retry_in number of
118
+ # seconds.
110
119
  # @option options [Array] :retry_statuses Array of Integer HTTP status
111
120
  # codes or a single Integer value that determines whether to raise
112
121
  # a Faraday::RetriableResponse exception based on the HTTP status code
113
122
  # of an HTTP response.
123
+ # @option options [Block] :header_parser_block block that will receive
124
+ # the the value of the retry header and should return the number of
125
+ # seconds to wait before retrying the request. This is useful if the
126
+ # value of the header is not a number of seconds or a RFC 2822 formatted date.
114
127
  def initialize(app, options = nil)
115
128
  super(app)
116
129
  @options = Options.from(options)
@@ -118,7 +131,7 @@ module Faraday
118
131
  end
119
132
 
120
133
  def calculate_sleep_amount(retries, env)
121
- retry_after = calculate_retry_after(env)
134
+ retry_after = [calculate_retry_after(env), calculate_rate_limit_reset(env)].compact.max
122
135
  retry_interval = calculate_retry_interval(retries)
123
136
 
124
137
  return if retry_after && retry_after > @options.max_interval
@@ -145,7 +158,13 @@ module Faraday
145
158
  retries -= 1
146
159
  rewind_files(request_body)
147
160
  if (sleep_amount = calculate_sleep_amount(retries + 1, env))
148
- @options.retry_block.call(env, @options, retries, e)
161
+ @options.retry_block.call(
162
+ env: env,
163
+ options: @options,
164
+ retry_count: @options.max - (retries + 1),
165
+ exception: e,
166
+ will_retry_in: sleep_amount
167
+ )
149
168
  sleep sleep_amount
150
169
  retry
151
170
  end
@@ -190,29 +209,26 @@ module Faraday
190
209
  end
191
210
 
192
211
  def rewind_files(body)
193
- return unless defined?(UploadIO)
212
+ return unless defined?(Faraday::UploadIO)
194
213
  return unless body.is_a?(Hash)
195
214
 
196
215
  body.each do |_, value|
197
- value.rewind if value.is_a?(UploadIO)
216
+ value.rewind if value.is_a?(Faraday::UploadIO)
198
217
  end
199
218
  end
200
219
 
220
+ # RFC for RateLimit Header Fields for HTTP:
221
+ # https://www.ietf.org/archive/id/draft-ietf-httpapi-ratelimit-headers-05.html#name-fields-definition
222
+ def calculate_rate_limit_reset(env)
223
+ reset_header = @options.rate_limit_reset_header || 'RateLimit-Reset'
224
+ parse_retry_header(env, reset_header)
225
+ end
226
+
201
227
  # MDN spec for Retry-After header:
202
228
  # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
203
229
  def calculate_retry_after(env)
204
- response_headers = env[:response_headers]
205
- return unless response_headers
206
-
207
- retry_after_value = env[:response_headers]['Retry-After']
208
-
209
- # Try to parse date from the header value
210
- begin
211
- datetime = DateTime.rfc2822(retry_after_value)
212
- datetime.to_time - Time.now.utc
213
- rescue ArgumentError
214
- retry_after_value.to_f
215
- end
230
+ retry_header = @options.rate_limit_retry_header || 'Retry-After'
231
+ parse_retry_header(env, retry_header)
216
232
  end
217
233
 
218
234
  def calculate_retry_interval(retries)
@@ -225,6 +241,25 @@ module Faraday
225
241
 
226
242
  current_interval + random_interval
227
243
  end
244
+
245
+ def parse_retry_header(env, header)
246
+ response_headers = env[:response_headers]
247
+ return unless response_headers
248
+
249
+ retry_after_value = env[:response_headers][header]
250
+
251
+ if @options.header_parser_block
252
+ @options.header_parser_block.call(retry_after_value)
253
+ else
254
+ # Try to parse date from the header value
255
+ begin
256
+ datetime = DateTime.rfc2822(retry_after_value)
257
+ datetime.to_time - Time.now.utc
258
+ rescue ArgumentError
259
+ retry_after_value.to_f
260
+ end
261
+ end
262
+ end
228
263
  end
229
264
  end
230
265
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Faraday
4
4
  module Retry
5
- VERSION = '1.0.3'
5
+ VERSION = '2.2.1'
6
6
  end
7
7
  end
data/lib/faraday/retry.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'faraday'
3
4
  require_relative 'retriable_response'
4
5
  require_relative 'retry/middleware'
5
6
  require_relative 'retry/version'
metadata CHANGED
@@ -1,15 +1,141 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday-retry
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 2.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mattia Giuffrida
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-06 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2024-04-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.21.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.21.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 1.21.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.21.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-packaging
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.5.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.5.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-performance
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.0'
13
139
  description: 'Catches exceptions and retries each request a limited number of times.
14
140
 
15
141
  '
@@ -31,11 +157,10 @@ licenses:
31
157
  - MIT
32
158
  metadata:
33
159
  bug_tracker_uri: https://github.com/lostisland/faraday-retry/issues
34
- changelog_uri: https://github.com/lostisland/faraday-retry/blob/v1.0.3/CHANGELOG.md
35
- documentation_uri: http://www.rubydoc.info/gems/faraday-retry/1.0.3
160
+ changelog_uri: https://github.com/lostisland/faraday-retry/blob/v2.2.1/CHANGELOG.md
161
+ documentation_uri: http://www.rubydoc.info/gems/faraday-retry/2.2.1
36
162
  homepage_uri: https://github.com/lostisland/faraday-retry
37
163
  source_code_uri: https://github.com/lostisland/faraday-retry
38
- wiki_uri: https://github.com/lostisland/faraday-retry/wiki
39
164
  post_install_message:
40
165
  rdoc_options: []
41
166
  require_paths:
@@ -44,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
44
169
  requirements:
45
170
  - - ">="
46
171
  - !ruby/object:Gem::Version
47
- version: '2.4'
172
+ version: '2.6'
48
173
  - - "<"
49
174
  - !ruby/object:Gem::Version
50
175
  version: '4'