faraday 2.3.0 → 2.7.4

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: 51764b6108e230dfff244e6e6e9f275f92f5fc1bcd1bf8e43d36058d35442e14
4
- data.tar.gz: 2cdf2edfd3e3c0386f501c0c2609db4f5c879a2a1807869a7b7e690e4a22a77a
3
+ metadata.gz: acdabcdd0a13d304292f17cd6764fba01d12f552cf78bdae3006ee38a3921ad0
4
+ data.tar.gz: 13300a1971d6557a4c62f01c4b0c96d1aecb4372b4c8a1ba734022109d9f2c0b
5
5
  SHA512:
6
- metadata.gz: 94ce1d0df0639d8e7a8472085331796a787236989e07943a47e463f6d5c68d83bf180b55636e87d37ac654400fe11660ee10898c9b43122c29a211310682b621
7
- data.tar.gz: 14af6c0c348cf3ca8ae7f7f8a95b3eb8b73b8a444d90a75c7eeb46d9edd4c840fa44dd2eda4e76653fd4924e6b20d13f32236e21bd303f90b589df1abc88da3d
6
+ metadata.gz: 0151023e942ae76b69a6bb28e70c792501333bc4f67c670bb990e80917c6b4d63aaa9ee168bd333abff2dd2b1fb54f244568e7a54d5346034d311924b17eabfe
7
+ data.tar.gz: 9598f16ed0c19cdfe01be90a1a29e6751b6848d44eac1d481224fbe576a33ff73f631a2bd398f70b5534d4126f8dba9ced68bb17dad5ea8efe93214bf7ce4e1b
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2020 Rick Olson, Zack Hobson
1
+ Copyright (c) 2009-2023 Rick Olson, Zack Hobson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![GitHub Actions CI](https://github.com/lostisland/faraday/workflows/CI/badge.svg)](https://github.com/lostisland/faraday/actions?query=workflow%3ACI)
5
5
  [![GitHub Discussions](https://img.shields.io/github/discussions/lostisland/faraday?logo=github)](https://github.com/lostisland/faraday/discussions)
6
6
 
7
-
8
7
  Faraday is an HTTP client library abstraction layer that provides a common interface over many
9
8
  adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.
10
9
  You probably don't want to use Faraday directly in your project, as it will lack an actual client library to perform
@@ -42,14 +41,15 @@ Open the issues page and check for the `help wanted` label!
42
41
  But before you start coding, please read our [Contributing Guide][contributing]
43
42
 
44
43
  ## Copyright
45
- © 2009 - 2021, the [Faraday Team][faraday_team]. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
46
44
 
47
- [awesome]: https://github.com/lostisland/awesome-faraday/#adapters
48
- [website]: https://lostisland.github.io/faraday
45
+ © 2009 - 2023, the [Faraday Team][faraday_team]. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
46
+
47
+ [awesome]: https://github.com/lostisland/awesome-faraday/#adapters
48
+ [website]: https://lostisland.github.io/faraday
49
49
  [faraday_team]: https://lostisland.github.io/faraday/team
50
50
  [contributing]: https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md
51
- [apidoc]: https://www.rubydoc.info/github/lostisland/faraday
52
- [actions]: https://github.com/lostisland/faraday/actions
53
- [jruby]: http://jruby.org/
54
- [rubinius]: http://rubini.us/
55
- [license]: LICENSE.md
51
+ [apidoc]: https://www.rubydoc.info/github/lostisland/faraday
52
+ [actions]: https://github.com/lostisland/faraday/actions
53
+ [jruby]: http://jruby.org/
54
+ [rubinius]: http://rubini.us/
55
+ [license]: LICENSE.md
@@ -17,6 +17,11 @@ class Client
17
17
  data = JSON.parse(res.body)
18
18
  data['origin']
19
19
  end
20
+
21
+ def foo(params)
22
+ res = @conn.post('/foo', JSON.dump(params))
23
+ res.status
24
+ end
20
25
  end
21
26
 
22
27
  RSpec.describe Client do
@@ -94,4 +99,21 @@ RSpec.describe Client do
94
99
  stubs.verify_stubbed_calls
95
100
  end
96
101
  end
102
+
103
+ context 'When you want to test the body, you can use a proc as well as string' do
104
+ it 'tests with a string' do
105
+ stubs.post('/foo', '{"name":"YK"}') { [200, {}, ''] }
106
+
107
+ expect(client.foo(name: 'YK')).to eq 200
108
+ stubs.verify_stubbed_calls
109
+ end
110
+
111
+ it 'tests with a proc' do
112
+ check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }
113
+ stubs.post('/foo', check) { [200, {}, ''] }
114
+
115
+ expect(client.foo(name: 'YK', created_at: Time.now)).to eq 200
116
+ stubs.verify_stubbed_calls
117
+ end
118
+ end
97
119
  end
@@ -18,6 +18,11 @@ class Client
18
18
  data = JSON.parse(res.body)
19
19
  data['origin']
20
20
  end
21
+
22
+ def foo(params)
23
+ res = @conn.post('/foo', JSON.dump(params))
24
+ res.status
25
+ end
21
26
  end
22
27
 
23
28
  # Example API client test
@@ -109,6 +114,27 @@ class ClientTest < Test::Unit::TestCase
109
114
  stubs.verify_stubbed_calls
110
115
  end
111
116
 
117
+ def test_with_string_body
118
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
119
+ stub.post('/foo', '{"name":"YK"}') { [200, {}, ''] }
120
+ end
121
+ cli = client(stubs)
122
+ assert_equal 200, cli.foo(name: 'YK')
123
+
124
+ stubs.verify_stubbed_calls
125
+ end
126
+
127
+ def test_with_proc_body
128
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
129
+ check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }
130
+ stub.post('/foo', check) { [200, {}, ''] }
131
+ end
132
+ cli = client(stubs)
133
+ assert_equal 200, cli.foo(name: 'YK', created_at: Time.now)
134
+
135
+ stubs.verify_stubbed_calls
136
+ end
137
+
112
138
  def client(stubs)
113
139
  conn = Faraday.new do |builder|
114
140
  builder.adapter :test, stubs
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'timeout'
4
+
3
5
  module Faraday
4
6
  class Adapter
5
7
  # @example
@@ -26,6 +28,15 @@ module Faraday
26
28
  # ]
27
29
  # end
28
30
  #
31
+ # # Test the request body is the same as the stubbed body
32
+ # stub.post('/bar', 'name=YK&word=call') { [200, {}, ''] }
33
+ #
34
+ # # You can pass a proc as a stubbed body and check the request body in your way.
35
+ # # In this case, the proc should return true or false.
36
+ # stub.post('/foo', ->(request_body) do
37
+ # JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }) { [200, {}, '']
38
+ # end
39
+ #
29
40
  # # You can set strict_mode to exactly match the stubbed requests.
30
41
  # stub.strict_mode = true
31
42
  # end
@@ -42,6 +53,12 @@ module Faraday
42
53
  #
43
54
  # resp = test.get '/items/2'
44
55
  # resp.body # => 'showing item: 2'
56
+ #
57
+ # resp = test.post '/bar', 'name=YK&word=call'
58
+ # resp.status # => 200
59
+ #
60
+ # resp = test.post '/foo', JSON.dump(name: 'YK', created_at: Time.now)
61
+ # resp.status # => 200
45
62
  class Test < Faraday::Adapter
46
63
  attr_accessor :stubs
47
64
 
@@ -181,7 +198,7 @@ module Faraday
181
198
  [(host.nil? || host == request_host) &&
182
199
  path_match?(request_path, meta) &&
183
200
  params_match?(env) &&
184
- (body.to_s.size.zero? || request_body == body) &&
201
+ body_match?(request_body) &&
185
202
  headers_match?(request_headers), meta]
186
203
  end
187
204
 
@@ -222,6 +239,17 @@ module Faraday
222
239
  end
223
240
  end
224
241
 
242
+ def body_match?(request_body)
243
+ return true if body.to_s.empty?
244
+
245
+ case body
246
+ when Proc
247
+ body.call(request_body)
248
+ else
249
+ request_body == body
250
+ end
251
+ end
252
+
225
253
  def to_s
226
254
  "#{path} #{body}"
227
255
  end
@@ -246,18 +274,33 @@ module Faraday
246
274
  stub, meta = stubs.match(env)
247
275
 
248
276
  unless stub
249
- raise Stubs::NotFound, "no stubbed request for #{env[:method]} "\
277
+ raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
250
278
  "#{env[:url]} #{env[:body]}"
251
279
  end
252
280
 
253
281
  block_arity = stub.block.arity
282
+ params = if block_arity >= 0
283
+ [env, meta].take(block_arity)
284
+ else
285
+ [env, meta]
286
+ end
287
+
288
+ timeout = request_timeout(:open, env[:request])
289
+ timeout ||= request_timeout(:read, env[:request])
290
+
254
291
  status, headers, body =
255
- if block_arity >= 0
256
- stub.block.call(*[env, meta].take(block_arity))
292
+ if timeout
293
+ ::Timeout.timeout(timeout, Faraday::TimeoutError) do
294
+ stub.block.call(*params)
295
+ end
257
296
  else
258
- stub.block.call(env, meta)
297
+ stub.block.call(*params)
259
298
  end
260
- save_response(env, status, body, headers)
299
+
300
+ # We need to explicitly pass `reason_phrase = nil` here to avoid keyword args conflicts.
301
+ # See https://github.com/lostisland/faraday/issues/1444
302
+ # TODO: remove `nil` explicit reason_phrase once Ruby 3.0 becomes minimum req. version
303
+ save_response(env, status, body, headers, nil)
261
304
 
262
305
  @app.call(env)
263
306
  end
@@ -59,7 +59,7 @@ module Faraday
59
59
 
60
60
  private
61
61
 
62
- def save_response(env, status, body, headers = nil, reason_phrase = nil)
62
+ def save_response(env, status, body, headers = nil, reason_phrase = nil, finished: true)
63
63
  env.status = status
64
64
  env.body = body
65
65
  env.reason_phrase = reason_phrase&.to_s&.strip
@@ -68,7 +68,7 @@ module Faraday
68
68
  yield(response_headers) if block_given?
69
69
  end
70
70
 
71
- env.response.finish(env) unless env.parallel?
71
+ env.response.finish(env) unless env.parallel? || !finished
72
72
  env.response
73
73
  end
74
74
 
@@ -78,8 +78,7 @@ module Faraday
78
78
  # @param type [Symbol] Describes which timeout setting to get: :read,
79
79
  # :write, or :open.
80
80
  # @param options [Hash] Hash containing Symbol keys like :timeout,
81
- # :read_timeout, :write_timeout, :open_timeout, or
82
- # :timeout
81
+ # :read_timeout, :write_timeout, or :open_timeout
83
82
  #
84
83
  # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
85
84
  # has been set.
@@ -130,10 +130,10 @@ module Faraday
130
130
  # Makes a GET HTTP request without a body.
131
131
  # @!scope class
132
132
  #
133
- # @param url [String] The optional String base URL to use as a prefix for
133
+ # @param url [String, URI, nil] The optional String base URL to use as a prefix for
134
134
  # all requests. Can also be the options Hash.
135
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
136
- # @param headers [Hash] unencoded HTTP header key/value pairs.
135
+ # @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
136
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
137
137
  #
138
138
  # @example
139
139
  # conn.get '/items', { page: 1 }, :accept => 'application/json'
@@ -152,10 +152,10 @@ module Faraday
152
152
  # Makes a HEAD HTTP request without a body.
153
153
  # @!scope class
154
154
  #
155
- # @param url [String] The optional String base URL to use as a prefix for
155
+ # @param url [String, URI, nil] The optional String base URL to use as a prefix for
156
156
  # all requests. Can also be the options Hash.
157
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
158
- # @param headers [Hash] unencoded HTTP header key/value pairs.
157
+ # @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
158
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
159
159
  #
160
160
  # @example
161
161
  # conn.head '/items/1'
@@ -167,10 +167,10 @@ module Faraday
167
167
  # Makes a DELETE HTTP request without a body.
168
168
  # @!scope class
169
169
  #
170
- # @param url [String] The optional String base URL to use as a prefix for
170
+ # @param url [String, URI, nil] The optional String base URL to use as a prefix for
171
171
  # all requests. Can also be the options Hash.
172
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
173
- # @param headers [Hash] unencoded HTTP header key/value pairs.
172
+ # @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
173
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
174
174
  #
175
175
  # @example
176
176
  # conn.delete '/items/1'
@@ -182,10 +182,10 @@ module Faraday
182
182
  # Makes a TRACE HTTP request without a body.
183
183
  # @!scope class
184
184
  #
185
- # @param url [String] The optional String base URL to use as a prefix for
185
+ # @param url [String, URI, nil] The optional String base URL to use as a prefix for
186
186
  # all requests. Can also be the options Hash.
187
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
188
- # @param headers [Hash] unencoded HTTP header key/value pairs.
187
+ # @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
188
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
189
189
  #
190
190
  # @example
191
191
  # conn.connect '/items/1'
@@ -210,9 +210,9 @@ module Faraday
210
210
  #
211
211
  # @overload options(url, params = nil, headers = nil)
212
212
  # Makes an OPTIONS HTTP request to the given URL.
213
- # @param url [String] String base URL to sue as a prefix for all requests.
214
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
215
- # @param headers [Hash] unencoded HTTP header key/value pairs.
213
+ # @param url [String, URI, nil] String base URL to sue as a prefix for all requests.
214
+ # @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
215
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
216
216
  #
217
217
  # @example
218
218
  # conn.options '/items/1'
@@ -220,7 +220,7 @@ module Faraday
220
220
  # @yield [Faraday::Request] for further request customizations
221
221
  # @return [Faraday::Response]
222
222
  def options(*args)
223
- return @options if args.size.zero?
223
+ return @options if args.empty?
224
224
 
225
225
  url, params, headers = *args
226
226
  run_request(:options, url, nil, headers) do |request|
@@ -233,10 +233,10 @@ module Faraday
233
233
  # Makes a POST HTTP request with a body.
234
234
  # @!scope class
235
235
  #
236
- # @param url [String] The optional String base URL to use as a prefix for
236
+ # @param url [String, URI, nil] The optional String base URL to use as a prefix for
237
237
  # all requests. Can also be the options Hash.
238
- # @param body [String] body for the request.
239
- # @param headers [Hash] unencoded HTTP header key/value pairs.
238
+ # @param body [String, nil] body for the request.
239
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
240
240
  #
241
241
  # @example
242
242
  # conn.post '/items', data, content_type: 'application/json'
@@ -255,20 +255,19 @@ module Faraday
255
255
  # Makes a PUT HTTP request with a body.
256
256
  # @!scope class
257
257
  #
258
- # @param url [String] The optional String base URL to use as a prefix for
258
+ # @param url [String, URI, nil] The optional String base URL to use as a prefix for
259
259
  # all requests. Can also be the options Hash.
260
- # @param body [String] body for the request.
261
- # @param headers [Hash] unencoded HTTP header key/value pairs.
260
+ # @param body [String, nil] body for the request.
261
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
262
262
  #
263
263
  # @example
264
- # # TODO: Make it a PUT example
265
- # conn.post '/items', data, content_type: 'application/json'
264
+ # conn.put '/products/123', data, content_type: 'application/json'
266
265
  #
267
- # # Simple ElasticSearch indexing sample.
268
- # conn.post '/twitter/tweet' do |req|
269
- # req.headers[:content_type] = 'application/json'
270
- # req.params[:routing] = 'kimchy'
271
- # req.body = JSON.generate(user: 'kimchy', ...)
266
+ # # Star a gist.
267
+ # conn.put 'https://api.github.com/gists/GIST_ID/star' do |req|
268
+ # req.headers['Accept'] = 'application/vnd.github+json'
269
+ # req.headers['Authorization'] = 'Bearer <YOUR-TOKEN>'
270
+ # req.headers['X-GitHub-Api-Version'] = '2022-11-28'
272
271
  # end
273
272
  #
274
273
  # @yield [Faraday::Request] for further request customizations
@@ -390,7 +389,7 @@ module Faraday
390
389
  # Takes a relative url for a request and combines it with the defaults
391
390
  # set on the connection instance.
392
391
  #
393
- # @param url [String]
392
+ # @param url [String, URI, nil]
394
393
  # @param extra_params [Hash]
395
394
  #
396
395
  # @example
@@ -423,10 +422,10 @@ module Faraday
423
422
  # Builds and runs the Faraday::Request.
424
423
  #
425
424
  # @param method [Symbol] HTTP method.
426
- # @param url [String, URI] String or URI to access.
427
- # @param body [Object] The request body that will eventually be converted to
425
+ # @param url [String, URI, nil] String or URI to access.
426
+ # @param body [String, nil] The request body that will eventually be converted to
428
427
  # a string.
429
- # @param headers [Hash] unencoded HTTP header key/value pairs.
428
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
430
429
  #
431
430
  # @return [Faraday::Response]
432
431
  def run_request(method, url, body, headers)
@@ -462,7 +461,7 @@ module Faraday
462
461
 
463
462
  # Build an absolute URL based on url_prefix.
464
463
  #
465
- # @param url [String, URI]
464
+ # @param url [String, URI, nil]
466
465
  # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
467
466
  # replace the query values
468
467
  # of the resulting url (default: nil).
@@ -471,10 +470,10 @@ module Faraday
471
470
  def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
472
471
  url = nil if url.respond_to?(:empty?) && url.empty?
473
472
  base = url_prefix.dup
474
- if url && base.path && base.path !~ %r{/$}
473
+ if url && !base.path.end_with?('/')
475
474
  base.path = "#{base.path}/" # ensure trailing slash
476
475
  end
477
- url = url.to_s.gsub(':', '%3A') if url && URI.parse(url.to_s).opaque
476
+ url = url.to_s.gsub(':', '%3A') if URI.parse(url.to_s).opaque
478
477
  uri = url ? base + url : base
479
478
  if params
480
479
  uri.query = params.to_query(params_encoder || options.params_encoder)
@@ -537,7 +536,7 @@ module Faraday
537
536
  end
538
537
 
539
538
  def find_default_proxy
540
- uri = ENV['http_proxy']
539
+ uri = ENV.fetch('http_proxy', nil)
541
540
  return unless uri && !uri.empty?
542
541
 
543
542
  uri = "http://#{uri}" unless uri.match?(/^http/i)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pp'
3
+ require 'pp' # rubocop:disable Lint/RedundantRequireStatement
4
4
 
5
5
  module Faraday
6
6
  module Logging
@@ -8,7 +8,7 @@ module Faraday
8
8
  class Formatter
9
9
  extend Forwardable
10
10
 
11
- DEFAULT_OPTIONS = { headers: true, bodies: false,
11
+ DEFAULT_OPTIONS = { headers: true, bodies: false, errors: false,
12
12
  log_level: :info }.freeze
13
13
 
14
14
  def initialize(logger:, options:)
@@ -37,6 +37,18 @@ module Faraday
37
37
  log_body('response', env[:body]) if env[:body] && log_body?(:response)
38
38
  end
39
39
 
40
+ def exception(exc)
41
+ return unless log_errors?
42
+
43
+ error_log = proc { exc.full_message }
44
+ public_send(log_level, 'error', &error_log)
45
+
46
+ log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error)
47
+ return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error)
48
+
49
+ log_body('error', exc.response_body)
50
+ end
51
+
40
52
  def filter(filter_word, filter_replacement)
41
53
  @filter.push([filter_word, filter_replacement])
42
54
  end
@@ -77,6 +89,10 @@ module Faraday
77
89
  end
78
90
  end
79
91
 
92
+ def log_errors?
93
+ @options[:errors]
94
+ end
95
+
80
96
  def apply_filters(output)
81
97
  @filter.each do |pattern, replacement|
82
98
  output = output.to_s.gsub(pattern, replacement)
@@ -17,6 +17,9 @@ module Faraday
17
17
  app.call(env).on_complete do |environment|
18
18
  on_complete(environment) if respond_to?(:on_complete)
19
19
  end
20
+ rescue StandardError => e
21
+ on_error(e) if respond_to?(:on_error)
22
+ raise
20
23
  end
21
24
 
22
25
  def close
@@ -15,13 +15,19 @@ module Faraday
15
15
  # @return [Hash] options for configuring the request.
16
16
  # Options for configuring the request.
17
17
  #
18
- # - `:timeout` open/read timeout Integer in seconds
19
- # - `:open_timeout` - read timeout Integer in seconds
20
- # - `:on_data` - Proc for streaming
21
- # - `:proxy` - Hash of proxy options
22
- # - `:uri` - Proxy Server URI
23
- # - `:user` - Proxy server username
24
- # - `:password` - Proxy server password
18
+ # - `:timeout` - time limit for the entire request (Integer in
19
+ # seconds)
20
+ # - `:open_timeout` - time limit for just the connection phase (e.g.
21
+ # handshake) (Integer in seconds)
22
+ # - `:read_timeout` - time limit for the first response byte received from
23
+ # the server (Integer in seconds)
24
+ # - `:write_timeout` - time limit for the client to send the request to the
25
+ # server (Integer in seconds)
26
+ # - `:on_data` - Proc for streaming
27
+ # - `:proxy` - Hash of proxy options
28
+ # - `:uri` - Proxy server URI
29
+ # - `:user` - Proxy server username
30
+ # - `:password` - Proxy server password
25
31
  #
26
32
  # @!attribute request_headers
27
33
  # @return [Hash] HTTP Headers to be sent to the server.
@@ -157,6 +163,24 @@ module Faraday
157
163
  %(#<#{self.class}#{attrs.join(' ')}>)
158
164
  end
159
165
 
166
+ def stream_response?
167
+ request.stream_response?
168
+ end
169
+
170
+ def stream_response(&block)
171
+ size = 0
172
+ yielded = false
173
+ block_result = block.call do |chunk| # rubocop:disable Performance/RedundantBlockCall
174
+ if chunk.bytesize.positive? || size.positive?
175
+ yielded = true
176
+ size += chunk.bytesize
177
+ request.on_data.call(chunk, size, self)
178
+ end
179
+ end
180
+ request.on_data.call(+'', 0, self) unless yielded
181
+ block_result
182
+ end
183
+
160
184
  # @private
161
185
  def custom_members
162
186
  @custom_members ||= {}
@@ -6,6 +6,10 @@ module Faraday
6
6
  # @!attribute verify
7
7
  # @return [Boolean] whether to verify SSL certificates or not
8
8
  #
9
+ # @!attribute verify_hostname
10
+ # @return [Boolean] whether to enable hostname verification on server certificates
11
+ # during the handshake or not (see https://github.com/ruby/openssl/pull/60)
12
+ #
9
13
  # @!attribute ca_file
10
14
  # @return [String] CA file
11
15
  #
@@ -41,7 +45,8 @@ module Faraday
41
45
  #
42
46
  # @!attribute max_version
43
47
  # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
44
- class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
48
+ class SSLOptions < Options.new(:verify, :verify_hostname,
49
+ :ca_file, :ca_path, :verify_mode,
45
50
  :cert_store, :client_cert, :client_key,
46
51
  :certificate, :private_key, :verify_depth,
47
52
  :version, :min_version, :max_version)
@@ -55,5 +60,10 @@ module Faraday
55
60
  def disable?
56
61
  !verify?
57
62
  end
63
+
64
+ # @return [Boolean] true if should verify_hostname
65
+ def verify_hostname?
66
+ verify_hostname != false
67
+ end
58
68
  end
59
69
  end
@@ -23,22 +23,27 @@ module Faraday
23
23
  def on_request(env)
24
24
  return if env.request_headers[KEY]
25
25
 
26
- env.request_headers[KEY] = header_from(@type, *@params)
26
+ env.request_headers[KEY] = header_from(@type, env, *@params)
27
27
  end
28
28
 
29
29
  private
30
30
 
31
31
  # @param type [String, Symbol]
32
+ # @param env [Faraday::Env]
32
33
  # @param params [Array]
33
34
  # @return [String] a header value
34
- def header_from(type, *params)
35
+ def header_from(type, env, *params)
35
36
  if type.to_s.casecmp('basic').zero? && params.size == 2
36
37
  Utils.basic_header_from(*params)
37
38
  elsif params.size != 1
38
39
  raise ArgumentError, "Unexpected params received (got #{params.size} instead of 1)"
39
40
  else
40
41
  value = params.first
41
- value = value.call if value.is_a?(Proc) || value.respond_to?(:call)
42
+ if (value.is_a?(Proc) && value.arity == 1) || (value.respond_to?(:call) && value.method(:call).arity == 1)
43
+ value = value.call(env)
44
+ elsif value.is_a?(Proc) || value.respond_to?(:call)
45
+ value = value.call
46
+ end
42
47
  "#{type} #{value}"
43
48
  end
44
49
  end
@@ -21,7 +21,7 @@ module Faraday
21
21
  # @!attribute headers
22
22
  # @return [Faraday::Utils::Headers] headers
23
23
  # @!attribute body
24
- # @return [Hash] body
24
+ # @return [String] body
25
25
  # @!attribute options
26
26
  # @return [RequestOptions] options
27
27
  #
@@ -26,6 +26,10 @@ module Faraday
26
26
  def on_complete(env)
27
27
  @formatter.response(env)
28
28
  end
29
+
30
+ def on_error(exc)
31
+ @formatter.exception(exc) if @formatter.respond_to?(:exception)
32
+ end
29
33
  end
30
34
  end
31
35
  end
@@ -61,7 +61,8 @@ module Faraday
61
61
  def to_hash
62
62
  {
63
63
  status: env.status, body: env.body,
64
- response_headers: env.response_headers
64
+ response_headers: env.response_headers,
65
+ url: env.url
65
66
  }
66
67
  end
67
68
 
@@ -132,7 +132,12 @@ module Faraday
132
132
 
133
133
  # Join multiple values with a comma.
134
134
  def add_parsed(key, value)
135
- self[key] ? self[key] << ', ' << value : self[key] = value
135
+ if key?(key)
136
+ self[key] = self[key].to_s
137
+ self[key] << ', ' << value
138
+ else
139
+ self[key] = value
140
+ end
136
141
  end
137
142
  end
138
143
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- VERSION = '2.3.0'
4
+ VERSION = '2.7.4'
5
5
  end
@@ -373,5 +373,70 @@ RSpec.describe Faraday::Adapter::Test do
373
373
  it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent' }
374
374
  it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
375
375
  end
376
+
377
+ describe 'body_match?' do
378
+ let(:stubs) do
379
+ described_class::Stubs.new do |stubs|
380
+ stubs.post('/no_check') { [200, {}, 'ok'] }
381
+ stubs.post('/with_string', 'abc') { [200, {}, 'ok'] }
382
+ stubs.post(
383
+ '/with_proc',
384
+ ->(request_body) { JSON.parse(request_body, symbolize_names: true) == { x: '!', a: [{ m: [{ a: true }], n: 123 }] } },
385
+ { content_type: 'application/json' }
386
+ ) do
387
+ [200, {}, 'ok']
388
+ end
389
+ end
390
+ end
391
+
392
+ context 'when trying without any args for body' do
393
+ subject(:without_body) { connection.post('/no_check') }
394
+
395
+ it { expect(without_body.status).to eq 200 }
396
+ end
397
+
398
+ context 'when trying with string body stubs' do
399
+ subject(:with_string) { connection.post('/with_string', 'abc') }
400
+
401
+ it { expect(with_string.status).to eq 200 }
402
+ end
403
+
404
+ context 'when trying with proc body stubs' do
405
+ subject(:with_proc) do
406
+ connection.post('/with_proc', JSON.dump(a: [{ n: 123, m: [{ a: true }] }], x: '!'), { 'Content-Type' => 'application/json' })
407
+ end
408
+
409
+ it { expect(with_proc.status).to eq 200 }
410
+ end
411
+ end
412
+ end
413
+
414
+ describe 'request timeout' do
415
+ subject(:request) do
416
+ connection.get('/sleep') do |req|
417
+ req.options.timeout = timeout
418
+ end
419
+ end
420
+
421
+ before do
422
+ stubs.get('/sleep') do
423
+ sleep(0.01)
424
+ [200, {}, '']
425
+ end
426
+ end
427
+
428
+ context 'when request is within timeout' do
429
+ let(:timeout) { 1 }
430
+
431
+ it { expect(request.status).to eq 200 }
432
+ end
433
+
434
+ context 'when request is too slow' do
435
+ let(:timeout) { 0.001 }
436
+
437
+ it 'raises an exception' do
438
+ expect { request }.to raise_error(Faraday::TimeoutError)
439
+ end
440
+ end
376
441
  end
377
442
  end
@@ -6,7 +6,7 @@ class CustomEncoder
6
6
  end
7
7
 
8
8
  def decode(params)
9
- params.split(',').map { |pair| pair.split('-') }.to_h
9
+ params.split(',').to_h { |pair| pair.split('-') }
10
10
  end
11
11
  end
12
12
 
@@ -131,6 +131,12 @@ RSpec.describe Faraday::Connection do
131
131
  it { expect(subject.ssl.verify?).to be_falsey }
132
132
  end
133
133
 
134
+ context 'with verify_hostname false' do
135
+ let(:options) { { ssl: { verify_hostname: false } } }
136
+
137
+ it { expect(subject.ssl.verify_hostname?).to be_falsey }
138
+ end
139
+
134
140
  context 'with empty block' do
135
141
  let(:conn) { Faraday::Connection.new {} }
136
142
 
@@ -304,6 +310,21 @@ RSpec.describe Faraday::Connection do
304
310
  expect(uri.to_s).to eq('http://service.com/api/service%3Asearch?limit=400')
305
311
  end
306
312
  end
313
+
314
+ context 'with a custom `default_uri_parser`' do
315
+ let(:url) { 'http://httpbingo.org' }
316
+ let(:parser) { Addressable::URI }
317
+
318
+ around do |example|
319
+ with_default_uri_parser(parser) do
320
+ example.run
321
+ end
322
+ end
323
+
324
+ it 'does not raise error' do
325
+ expect { conn.build_exclusive_url('/nigiri') }.not_to raise_error
326
+ end
327
+ end
307
328
  end
308
329
 
309
330
  describe '#build_url' do
@@ -505,7 +526,7 @@ RSpec.describe Faraday::Connection do
505
526
  it 'uses env http_proxy' do
506
527
  with_env 'http_proxy' => 'http://proxy.com' do
507
528
  conn = Faraday.new
508
- expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
529
+ expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey
509
530
  expect(conn.proxy_for_request('http://google.co.uk').host).to eq('proxy.com')
510
531
  end
511
532
  end
@@ -513,7 +534,7 @@ RSpec.describe Faraday::Connection do
513
534
  it 'uses processes no_proxy before http_proxy' do
514
535
  with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do
515
536
  conn = Faraday.new
516
- expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
537
+ expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey
517
538
  expect(conn.proxy_for_request('http://google.co.uk')).to be_nil
518
539
  end
519
540
  end
@@ -521,7 +542,7 @@ RSpec.describe Faraday::Connection do
521
542
  it 'uses env https_proxy' do
522
543
  with_env 'https_proxy' => 'https://proxy.com' do
523
544
  conn = Faraday.new
524
- expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
545
+ expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey
525
546
  expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy.com')
526
547
  end
527
548
  end
@@ -529,7 +550,7 @@ RSpec.describe Faraday::Connection do
529
550
  it 'uses processes no_proxy before https_proxy' do
530
551
  with_env 'https_proxy' => 'https://proxy.com', 'no_proxy' => 'google.co.uk' do
531
552
  conn = Faraday.new
532
- expect(conn.instance_variable_get('@manual_proxy')).to be_falsey
553
+ expect(conn.instance_variable_get(:@manual_proxy)).to be_falsey
533
554
  expect(conn.proxy_for_request('https://google.co.uk')).to be_nil
534
555
  end
535
556
  end
@@ -539,7 +560,7 @@ RSpec.describe Faraday::Connection do
539
560
  conn = Faraday.new
540
561
  conn.proxy = 'http://proxy2.com'
541
562
 
542
- expect(conn.instance_variable_get('@manual_proxy')).to be_truthy
563
+ expect(conn.instance_variable_get(:@manual_proxy)).to be_truthy
543
564
  expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy2.com')
544
565
  end
545
566
  end
@@ -574,7 +595,7 @@ RSpec.describe Faraday::Connection do
574
595
  end
575
596
 
576
597
  conn.get(url)
577
- expect(conn.instance_variable_get('@temp_proxy')).to be_nil
598
+ expect(conn.instance_variable_get(:@temp_proxy)).to be_nil
578
599
  end
579
600
 
580
601
  it 'dynamically check no proxy' do
@@ -33,6 +33,24 @@ RSpec.describe Faraday::Middleware do
33
33
  end
34
34
  end
35
35
 
36
+ describe '#on_error' do
37
+ subject do
38
+ Class.new(described_class) do
39
+ def on_error(error)
40
+ # do nothing
41
+ end
42
+ end.new(app)
43
+ end
44
+
45
+ it 'is called by #call' do
46
+ expect(app).to receive(:call).and_raise(Faraday::ConnectionFailed)
47
+ is_expected.to receive(:call).and_call_original
48
+ is_expected.to receive(:on_error)
49
+
50
+ expect { subject.call(double) }.to raise_error(Faraday::ConnectionFailed)
51
+ end
52
+ end
53
+
36
54
  describe '#close' do
37
55
  context "with app that doesn't support \#close" do
38
56
  it 'should issue warning' do
@@ -27,6 +27,12 @@ RSpec.describe Faraday::Env do
27
27
  expect(ssl.fetch(:verify, true)).to be_falsey
28
28
  end
29
29
 
30
+ it 'handle verify_hostname when fetching' do
31
+ ssl = Faraday::SSLOptions.new
32
+ ssl.verify_hostname = true
33
+ expect(ssl.fetch(:verify_hostname, false)).to be_truthy
34
+ end
35
+
30
36
  it 'retains custom members' do
31
37
  env[:foo] = 'custom 1'
32
38
  env[:bar] = :custom2
@@ -72,6 +72,41 @@ RSpec.describe Faraday::Request::Authorization do
72
72
  include_examples 'does not interfere with existing authentication'
73
73
  end
74
74
 
75
+ context 'with an argument' do
76
+ let(:response) { conn.get('/auth-echo', nil, 'middle' => 'crunchy surprise') }
77
+
78
+ context 'when passed a proc' do
79
+ let(:auth_config) { [proc { |env| "proc #{env.request_headers['middle']}" }] }
80
+
81
+ it { expect(response.body).to eq('Bearer proc crunchy surprise') }
82
+
83
+ include_examples 'does not interfere with existing authentication'
84
+ end
85
+
86
+ context 'when passed a lambda' do
87
+ let(:auth_config) { [->(env) { "lambda #{env.request_headers['middle']}" }] }
88
+
89
+ it { expect(response.body).to eq('Bearer lambda crunchy surprise') }
90
+
91
+ include_examples 'does not interfere with existing authentication'
92
+ end
93
+
94
+ context 'when passed a callable with an argument' do
95
+ let(:callable) do
96
+ Class.new do
97
+ def call(env)
98
+ "callable #{env.request_headers['middle']}"
99
+ end
100
+ end.new
101
+ end
102
+ let(:auth_config) { [callable] }
103
+
104
+ it { expect(response.body).to eq('Bearer callable crunchy surprise') }
105
+
106
+ include_examples 'does not interfere with existing authentication'
107
+ end
108
+ end
109
+
75
110
  context 'when passed too many arguments' do
76
111
  let(:auth_config) { %w[baz foo] }
77
112
 
@@ -14,6 +14,7 @@ RSpec.describe Faraday::Request do
14
14
  context 'when nothing particular is configured' do
15
15
  it { expect(subject.http_method).to eq(:get) }
16
16
  it { expect(subject.to_env(conn).ssl.verify).to be_falsey }
17
+ it { expect(subject.to_env(conn).ssl.verify_hostname).to be_falsey }
17
18
  end
18
19
 
19
20
  context 'when HTTP method is post' do
@@ -64,6 +64,15 @@ RSpec.describe Faraday::Response::Logger do
64
64
  expect(formatter).to receive(:response).with(an_instance_of(Faraday::Env))
65
65
  conn.get '/hello'
66
66
  end
67
+
68
+ context 'when no route' do
69
+ it 'delegates logging to the formatter' do
70
+ expect(formatter).to receive(:request).with(an_instance_of(Faraday::Env))
71
+ expect(formatter).to receive(:exception).with(an_instance_of(Faraday::Adapter::Test::Stubs::NotFound))
72
+
73
+ expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
74
+ end
75
+ end
67
76
  end
68
77
 
69
78
  context 'with custom formatter' do
@@ -94,6 +103,16 @@ RSpec.describe Faraday::Response::Logger do
94
103
  expect(string_io.string).to match('GET http:/hello')
95
104
  end
96
105
 
106
+ it 'logs status' do
107
+ conn.get '/hello', nil, accept: 'text/html'
108
+ expect(string_io.string).to match('Status 200')
109
+ end
110
+
111
+ it 'does not log error message by default' do
112
+ expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
113
+ expect(string_io.string).not_to match(%(no stubbed request for get http:/noroute))
114
+ end
115
+
97
116
  it 'logs request headers by default' do
98
117
  conn.get '/hello', nil, accept: 'text/html'
99
118
  expect(string_io.string).to match(%(Accept: "text/html))
@@ -188,6 +207,15 @@ RSpec.describe Faraday::Response::Logger do
188
207
  end
189
208
  end
190
209
 
210
+ context 'when logging errors' do
211
+ let(:logger_options) { { errors: true } }
212
+
213
+ it 'logs error message' do
214
+ expect { conn.get '/noroute' }.to raise_error(Faraday::Adapter::Test::Stubs::NotFound)
215
+ expect(string_io.string).to match(%(no stubbed request for get http:/noroute))
216
+ end
217
+ end
218
+
191
219
  context 'when using log_level' do
192
220
  let(:logger_options) { { bodies: true, log_level: :debug } }
193
221
 
@@ -4,7 +4,7 @@ RSpec.describe Faraday::Response do
4
4
  subject { Faraday::Response.new(env) }
5
5
 
6
6
  let(:env) do
7
- Faraday::Env.from(status: 404, body: 'yikes',
7
+ Faraday::Env.from(status: 404, body: 'yikes', url: Faraday::Utils.URI('https://lostisland.github.io/faraday'),
8
8
  response_headers: { 'Content-Type' => 'text/plain' })
9
9
  end
10
10
 
@@ -30,6 +30,7 @@ RSpec.describe Faraday::Response do
30
30
  it { expect(hash[:status]).to eq(subject.status) }
31
31
  it { expect(hash[:response_headers]).to eq(subject.headers) }
32
32
  it { expect(hash[:body]).to eq(subject.body) }
33
+ it { expect(hash[:url]).to eq(subject.env.url) }
33
34
  end
34
35
 
35
36
  describe 'marshal serialization support' do
@@ -45,6 +46,7 @@ RSpec.describe Faraday::Response do
45
46
  it { expect(loaded.env[:body]).to eq(env[:body]) }
46
47
  it { expect(loaded.env[:response_headers]).to eq(env[:response_headers]) }
47
48
  it { expect(loaded.env[:status]).to eq(env[:status]) }
49
+ it { expect(loaded.env[:url]).to eq(env[:url]) }
48
50
  end
49
51
 
50
52
  describe '#on_complete' do
@@ -57,11 +57,11 @@ RSpec.describe Faraday::Utils::Headers do
57
57
  end
58
58
 
59
59
  describe '#parse' do
60
- before { subject.parse(headers) }
61
-
62
60
  context 'when response headers leave http status line out' do
63
61
  let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" }
64
62
 
63
+ before { subject.parse(headers) }
64
+
65
65
  it { expect(subject.keys).to eq(%w[Content-Type]) }
66
66
  it { expect(subject['Content-Type']).to eq('text/html') }
67
67
  it { expect(subject['content-type']).to eq('text/html') }
@@ -70,13 +70,31 @@ RSpec.describe Faraday::Utils::Headers do
70
70
  context 'when response headers values include a colon' do
71
71
  let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://httpbingo.org/\r\n\r\n" }
72
72
 
73
+ before { subject.parse(headers) }
74
+
73
75
  it { expect(subject['location']).to eq('http://httpbingo.org/') }
74
76
  end
75
77
 
76
78
  context 'when response headers include a blank line' do
77
79
  let(:headers) { "HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n" }
78
80
 
81
+ before { subject.parse(headers) }
82
+
79
83
  it { expect(subject['content-type']).to eq('text/html') }
80
84
  end
85
+
86
+ context 'when response headers include already stored keys' do
87
+ let(:headers) { "HTTP/1.1 200 OK\r\nX-Numbers: 123\r\n\r\n" }
88
+
89
+ before do
90
+ h = subject
91
+ h[:x_numbers] = 8
92
+ h.parse(headers)
93
+ end
94
+
95
+ it do
96
+ expect(subject[:x_numbers]).to eq('8, 123')
97
+ end
98
+ end
81
99
  end
82
100
  end
@@ -102,7 +102,8 @@ RSpec.describe Faraday::Utils do
102
102
  verify_depth: nil,
103
103
  version: '2',
104
104
  min_version: nil,
105
- max_version: nil
105
+ max_version: nil,
106
+ verify_hostname: nil
106
107
  }
107
108
  end
108
109
 
@@ -37,7 +37,8 @@ shared_examples 'adapter examples' do |**options|
37
37
 
38
38
  let(:conn) do
39
39
  conn_options[:ssl] ||= {}
40
- conn_options[:ssl][:ca_file] ||= ENV['SSL_FILE']
40
+ conn_options[:ssl][:ca_file] ||= ENV.fetch('SSL_FILE', nil)
41
+ conn_options[:ssl][:verify_hostname] ||= ENV['SSL_VERIFY_HOSTNAME'] == 'yes'
41
42
 
42
43
  Faraday.new(remote, conn_options) do |conn|
43
44
  conn.request :url_encoded
@@ -153,12 +153,19 @@ shared_examples 'a request method' do |http_method|
153
153
  let(:streamed) { [] }
154
154
 
155
155
  context 'when response is empty' do
156
- it do
156
+ it 'handles streaming' do
157
+ env = nil
157
158
  conn.public_send(http_method, '/') do |req|
158
- req.options.on_data = proc { |*args| streamed << args }
159
+ req.options.on_data = proc do |chunk, size, block_env|
160
+ streamed << [chunk, size]
161
+ env ||= block_env
162
+ end
159
163
  end
160
164
 
161
165
  expect(streamed).to eq([['', 0]])
166
+ # TODO: enable this after updating all existing adapters to the new streaming API
167
+ # expect(env).to be_a(Faraday::Env)
168
+ # expect(env.status).to eq(200)
162
169
  end
163
170
  end
164
171
 
@@ -166,12 +173,19 @@ shared_examples 'a request method' do |http_method|
166
173
  before { request_stub.to_return(body: big_string) }
167
174
 
168
175
  it 'handles streaming' do
176
+ env = nil
169
177
  response = conn.public_send(http_method, '/') do |req|
170
- req.options.on_data = proc { |*args| streamed << args }
178
+ req.options.on_data = proc do |chunk, size, block_env|
179
+ streamed << [chunk, size]
180
+ env ||= block_env
181
+ end
171
182
  end
172
183
 
173
184
  expect(response.body).to eq('')
174
185
  check_streaming_response(streamed, chunk_size: 16 * 1024)
186
+ # TODO: enable this after updating all existing adapters to the new streaming API
187
+ # expect(env).to be_a(Faraday::Env)
188
+ # expect(env.status).to eq(200)
175
189
  end
176
190
  end
177
191
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: faraday
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - "@technoweenie"
@@ -10,22 +10,28 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-05-06 00:00:00.000000000 Z
13
+ date: 2023-01-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: faraday-net_http
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - "~>"
19
+ - - ">="
20
20
  - !ruby/object:Gem::Version
21
21
  version: '2.0'
22
+ - - "<"
23
+ - !ruby/object:Gem::Version
24
+ version: '3.1'
22
25
  type: :runtime
23
26
  prerelease: false
24
27
  version_requirements: !ruby/object:Gem::Requirement
25
28
  requirements:
26
- - - "~>"
29
+ - - ">="
27
30
  - !ruby/object:Gem::Version
28
31
  version: '2.0'
32
+ - - "<"
33
+ - !ruby/object:Gem::Version
34
+ version: '3.1'
29
35
  - !ruby/object:Gem::Dependency
30
36
  name: ruby2_keywords
31
37
  requirement: !ruby/object:Gem::Requirement
@@ -125,7 +131,7 @@ licenses:
125
131
  - MIT
126
132
  metadata:
127
133
  homepage_uri: https://lostisland.github.io/faraday
128
- changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.3.0
134
+ changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.7.4
129
135
  source_code_uri: https://github.com/lostisland/faraday
130
136
  bug_tracker_uri: https://github.com/lostisland/faraday/issues
131
137
  post_install_message: