faraday 2.14.3 → 2.15.0

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: ecc5e9592d7434d7e39bc5cc827be5b62af99e258582468c8b6fd78e0122bf7e
4
- data.tar.gz: 4f5728ac524a3456a800c8d0159b4569173b1c841f80fae7d0d2704e261b6588
3
+ metadata.gz: 1c06dbc8aa2e38f23986579d5521da86fb335aaf0f5aa83db6fefb530e645b9f
4
+ data.tar.gz: 84db1e0a961b108e53db54277bc22f533a19f1abf3475384e09a396bf98969d1
5
5
  SHA512:
6
- metadata.gz: 12567175cd7d08a95ccf8203bffcb9bebf8d1b52f279ce034c3f3e17dc5efc81e08259c2992ff792560ed626dd59ae305c163cb43018860b3c0ae833c3f68855
7
- data.tar.gz: bb786c688b4629c6bc43e4715715fcd42de120eee3802a3614eca60e17627ff77f40298af500a431158b1f1de8bd0d0bd58817a8793bab92e11bac9aa9b482f2
6
+ metadata.gz: dd4be1a725b5f287c0812ec1caa909c5338e938b697581d1bf585460fa47332d4dc840581edd726ff73fe2f4380fe65c45ba05fc92c98816954fe9ac356e0b23
7
+ data.tar.gz: a508f88684b8f60c9655ebec4acfc9e7cebe25982e708fc98be02d46357304c73ef408de7bba4e2c52b12229c76168c51d3cb9cc1e5728901d580bd6e986dca0
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2023 Rick Olson, Zack Hobson
1
+ Copyright (c) 2009-2026 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
@@ -58,7 +58,7 @@ But before you start coding, please read our [Contributing Guide][contributing]
58
58
 
59
59
  ## Copyright
60
60
 
61
- © 2009 - 2023, the Faraday Team. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
61
+ © 2009 - 2026, the Faraday Team. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
62
62
 
63
63
  [awesome]: https://github.com/lostisland/awesome-faraday/#adapters
64
64
  [website]: https://lostisland.github.io/faraday
@@ -210,7 +210,7 @@ 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, URI, nil] String base URL to sue as a prefix for all requests.
213
+ # @param url [String, URI, nil] String base URL to use as a prefix for all requests.
214
214
  # @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
215
215
  # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
216
216
  #
@@ -315,13 +315,13 @@ module Faraday
315
315
  # @yield a block to execute multiple requests.
316
316
  # @return [void]
317
317
  def in_parallel(manager = nil, &block)
318
- @parallel_manager = manager || default_parallel_manager do
319
- warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
320
- 'on Faraday stack'
321
- warn caller[2, 10].join("\n")
318
+ previous_manager = @parallel_manager
319
+ @parallel_manager = manager || previous_manager || default_parallel_manager do
320
+ warn('Warning: `in_parallel` called but no parallel-capable adapter ' \
321
+ 'on Faraday stack', caller[2, 10].join("\n"))
322
322
  nil
323
323
  end
324
- return yield unless @parallel_manager
324
+ return yield unless @parallel_manager && !@parallel_manager.equal?(previous_manager)
325
325
 
326
326
  if @parallel_manager.respond_to?(:execute)
327
327
  # Execute is the new method that is responsible for executing the block.
@@ -332,7 +332,7 @@ module Faraday
332
332
  @parallel_manager.run
333
333
  end
334
334
  ensure
335
- @parallel_manager = nil
335
+ @parallel_manager = previous_manager
336
336
  end
337
337
 
338
338
  # Sets the Hash proxy options.
@@ -505,7 +505,7 @@ module Faraday
505
505
  params: params.dup,
506
506
  builder: builder.dup,
507
507
  ssl: ssl.dup,
508
- request: options.dup)
508
+ request: options.dup).tap { |copy| copy.proxy = proxy if @manual_proxy }
509
509
  end
510
510
 
511
511
  # Yields username and password extracted from a URI if they both exist.
@@ -519,7 +519,7 @@ module Faraday
519
519
  def with_uri_credentials(uri)
520
520
  return unless uri.user && uri.password
521
521
 
522
- yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
522
+ yield(URI::DEFAULT_PARSER.unescape(uri.user), URI::DEFAULT_PARSER.unescape(uri.password))
523
523
  end
524
524
 
525
525
  def proxy_from_env(url)
@@ -532,7 +532,7 @@ module Faraday
532
532
  uri = if uri.host.nil?
533
533
  find_default_proxy
534
534
  else
535
- URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
535
+ URI.parse(uri.to_s).find_proxy
536
536
  end
537
537
  when URI
538
538
  uri = url.find_proxy
@@ -4,6 +4,33 @@ module Faraday
4
4
  # @!parse
5
5
  # # ConnectionOptions contains the configurable properties for a Faraday
6
6
  # # connection object.
7
+ # #
8
+ # # @!attribute request
9
+ # # @return [RequestOptions] default request options for the connection
10
+ # #
11
+ # # @!attribute proxy
12
+ # # @return [ProxyOptions] proxy options for the connection
13
+ # #
14
+ # # @!attribute ssl
15
+ # # @return [SSLOptions] SSL options for the connection
16
+ # #
17
+ # # @!attribute builder
18
+ # # @return [RackBuilder] the middleware stack builder for the connection
19
+ # #
20
+ # # @!attribute url
21
+ # # @return [String, URI] the base URL for the connection
22
+ # #
23
+ # # @!attribute parallel_manager
24
+ # # @return [Object] manager used when the connection runs in parallel mode
25
+ # #
26
+ # # @!attribute params
27
+ # # @return [Hash] default query parameters for the connection
28
+ # #
29
+ # # @!attribute headers
30
+ # # @return [Hash] default headers for the connection
31
+ # #
32
+ # # @!attribute builder_class
33
+ # # @return [Class] the class used to build the middleware stack
7
34
  # class ConnectionOptions < Options; end
8
35
  ConnectionOptions = Options.new(:request, :proxy, :ssl, :builder, :url,
9
36
  :parallel_manager, :params, :headers,
@@ -4,6 +4,15 @@ module Faraday
4
4
  # @!parse
5
5
  # # ProxyOptions contains the configurable properties for the proxy
6
6
  # # configuration used when making an HTTP request.
7
+ # #
8
+ # # @!attribute uri
9
+ # # @return [URI] the proxy server URI
10
+ # #
11
+ # # @!attribute user
12
+ # # @return [String] the proxy server username
13
+ # #
14
+ # # @!attribute password
15
+ # # @return [String] the proxy server password
7
16
  # class ProxyOptions < Options; end
8
17
  ProxyOptions = Options.new(:uri, :user, :password) do
9
18
  extend Forwardable
@@ -33,7 +42,7 @@ module Faraday
33
42
  super
34
43
  end
35
44
 
36
- memoized(:user) { uri&.user && Utils.unescape(uri.user) }
37
- memoized(:password) { uri&.password && Utils.unescape(uri.password) }
45
+ memoized(:user) { uri&.user && URI::DEFAULT_PARSER.unescape(uri.user) }
46
+ memoized(:password) { uri&.password && URI::DEFAULT_PARSER.unescape(uri.password) }
38
47
  end
39
48
  end
@@ -3,6 +3,41 @@
3
3
  module Faraday
4
4
  # @!parse
5
5
  # # RequestOptions contains the configurable properties for a Faraday request.
6
+ # #
7
+ # # @!attribute params_encoder
8
+ # # @return [Object] the params encoder used to serialize the request params
9
+ # #
10
+ # # @!attribute proxy
11
+ # # @return [ProxyOptions] proxy options for this request
12
+ # #
13
+ # # @!attribute bind
14
+ # # @return [Hash] the local host and port to bind the connection to
15
+ # #
16
+ # # @!attribute timeout
17
+ # # @return [Integer] time limit for the entire request (in seconds)
18
+ # #
19
+ # # @!attribute open_timeout
20
+ # # @return [Integer] time limit for just the connection phase (in seconds)
21
+ # #
22
+ # # @!attribute read_timeout
23
+ # # @return [Integer] time limit for the first response byte received from
24
+ # # the server (in seconds)
25
+ # #
26
+ # # @!attribute write_timeout
27
+ # # @return [Integer] time limit for the client to send the request to the
28
+ # # server (in seconds)
29
+ # #
30
+ # # @!attribute boundary
31
+ # # @return [String] the boundary used for multipart request bodies
32
+ # #
33
+ # # @!attribute oauth
34
+ # # @return [Hash] OAuth options used by the OAuth middleware
35
+ # #
36
+ # # @!attribute context
37
+ # # @return [Hash] free-form storage carried alongside the request
38
+ # #
39
+ # # @!attribute on_data
40
+ # # @return [Proc] callback invoked with each chunk when streaming the response
6
41
  # class RequestOptions < Options; end
7
42
  RequestOptions = Options.new(:params_encoder, :proxy, :bind,
8
43
  :timeout, :open_timeout, :read_timeout,
@@ -74,7 +74,7 @@ module Faraday
74
74
 
75
75
  # Public
76
76
  def fetch(key, *args)
77
- unless symbolized_key_set.include?(key.to_sym)
77
+ unless members.include?(key.to_sym) && !send(key).nil?
78
78
  key_setter = "#{key}="
79
79
  if !args.empty?
80
80
  send(key_setter, args.first)
@@ -193,7 +193,7 @@ module Faraday
193
193
  end
194
194
 
195
195
  def symbolized_key_set
196
- @symbolized_key_set ||= Set.new(keys.map(&:to_sym))
196
+ Set.new(keys.map(&:to_sym))
197
197
  end
198
198
 
199
199
  def self.inherited(subclass)
@@ -115,6 +115,7 @@ module Faraday
115
115
  def adapter(klass = NO_ARGUMENT, *args, **kwargs, &block)
116
116
  return @adapter if klass == NO_ARGUMENT || klass.nil?
117
117
 
118
+ raise_if_locked
118
119
  klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
119
120
  @adapter = self.class::Handler.new(klass, *args, **kwargs, &block)
120
121
  end
@@ -230,10 +231,6 @@ module Faraday
230
231
  raise MISSING_ADAPTER_ERROR unless @adapter
231
232
  end
232
233
 
233
- def adapter_set?
234
- !@adapter.nil?
235
- end
236
-
237
234
  def use_symbol(mod, key, ...)
238
235
  use(mod.lookup_middleware(key), ...)
239
236
  end
@@ -8,7 +8,7 @@ module Faraday
8
8
  class Json < Middleware
9
9
  def initialize(app = nil, parser_options: nil, content_type: /\bjson$/, preserve_raw: false)
10
10
  super(app)
11
- @parser_options = parser_options
11
+ @parser_options = parser_options&.dup
12
12
  @content_types = Array(content_type)
13
13
  @preserve_raw = preserve_raw
14
14
 
@@ -33,7 +33,7 @@ module Faraday
33
33
 
34
34
  decoder, method_name = @decoder_options
35
35
 
36
- decoder.public_send(method_name, body, @parser_options || {})
36
+ decoder.public_send(method_name, body, **(@parser_options || {}))
37
37
  end
38
38
 
39
39
  def parse_response?(env)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- VERSION = '2.14.3'
4
+ VERSION = '2.15.0'
5
5
  end
data/lib/faraday.rb CHANGED
@@ -137,6 +137,38 @@ module Faraday
137
137
  @default_connection_options = ConnectionOptions.from(options)
138
138
  end
139
139
 
140
+ # HTTP verb methods, forwarded to {.default_connection}.
141
+ #
142
+ # They take the same arguments as their {Faraday::Connection} counterparts,
143
+ # see {Faraday::Connection#get} and {Faraday::Connection#post}.
144
+ # Defining them here rather than relying on {method_missing} keeps them
145
+ # visible to `Faraday.methods`, `pry`'s `ls`, documentation tools and
146
+ # editor autocomplete.
147
+
148
+ # @!visibility private
149
+ METHODS_WITH_QUERY.each do |method|
150
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
151
+ # def get(url = nil, params = nil, headers = nil, &block)
152
+ # default_connection.get(url, params, headers, &block)
153
+ # end
154
+ def #{method}(url = nil, params = nil, headers = nil, &block)
155
+ default_connection.#{method}(url, params, headers, &block)
156
+ end
157
+ RUBY
158
+ end
159
+
160
+ # @!visibility private
161
+ METHODS_WITH_BODY.each do |method|
162
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
163
+ # def post(url = nil, body = nil, headers = nil, &block)
164
+ # default_connection.post(url, body, headers, &block)
165
+ # end
166
+ def #{method}(url = nil, body = nil, headers = nil, &block)
167
+ default_connection.#{method}(url, body, headers, &block)
168
+ end
169
+ RUBY
170
+ end
171
+
140
172
  private
141
173
 
142
174
  # Internal: Proxies method calls on the Faraday constant to
@@ -119,6 +119,15 @@ RSpec.describe Faraday::Connection do
119
119
  it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') }
120
120
  end
121
121
 
122
+ context 'with basic_auth in url containing literal plus signs' do
123
+ let(:url) { 'http://Ala+ddin:open+sesame%2B@httpbingo.org/fish' }
124
+
125
+ it 'does not decode plus signs as spaces' do
126
+ expected = Faraday::Utils.basic_header_from('Ala+ddin', 'open+sesame+')
127
+ expect(subject.headers['Authorization']).to eq(expected)
128
+ end
129
+ end
130
+
122
131
  context 'with custom headers' do
123
132
  let(:options) { { headers: { user_agent: 'Faraday' } } }
124
133
 
@@ -693,6 +702,34 @@ RSpec.describe Faraday::Connection do
693
702
  it { expect(subject.options[:timeout]).to eq(5) }
694
703
  it { expect(conn.options[:open_timeout]).to be_nil }
695
704
  end
705
+
706
+ context 'with a manually set proxy' do
707
+ it 'preserves the proxy over env http_proxy' do
708
+ with_env 'http_proxy' => 'http://env-proxy.com' do
709
+ conn.proxy = 'http://manual-proxy.com'
710
+
711
+ expect(subject.proxy.host).to eq('manual-proxy.com')
712
+ expect(subject.proxy_for_request(url).host).to eq('manual-proxy.com')
713
+ end
714
+ end
715
+
716
+ it 'preserves an explicitly disabled proxy' do
717
+ with_env 'http_proxy' => 'http://env-proxy.com' do
718
+ conn.proxy = nil
719
+
720
+ expect(subject.proxy).to be_nil
721
+ expect(subject.proxy_for_request(url)).to be_nil
722
+ end
723
+ end
724
+ end
725
+
726
+ context 'without a manually set proxy' do
727
+ it 'uses env http_proxy' do
728
+ with_env 'http_proxy' => 'http://env-proxy.com' do
729
+ expect(subject.proxy_for_request(url).host).to eq('env-proxy.com')
730
+ end
731
+ end
732
+ end
696
733
  end
697
734
 
698
735
  describe '#respond_to?' do
@@ -277,6 +277,40 @@ RSpec.describe Faraday::Options do
277
277
  it 'needs a default if key is missing' do
278
278
  expect { subject.fetch(:sub_a) }.to raise_error(Faraday::Options.fetch_error_class)
279
279
  end
280
+
281
+ it 'keeps the installed default on repeated fetches' do
282
+ expect(subject.fetch(:sub_a, 1)).to eq(1)
283
+ expect(subject.fetch(:sub_a, 2)).to eq(1)
284
+ end
285
+
286
+ it 'reads the current value after a default is replaced' do
287
+ subject.fetch(:sub_a, 1)
288
+ subject.sub_a = 3
289
+
290
+ expect(subject.fetch(:sub_a, 2)).to eq(3)
291
+ end
292
+
293
+ it 'installs a new default after deletion or clearing' do
294
+ subject.fetch(:sub_a, 1)
295
+ subject.delete(:sub_a)
296
+ expect(subject.fetch(:sub_a, 2)).to eq(2)
297
+
298
+ subject.clear
299
+ expect(subject.fetch(:sub_a, 3)).to eq(3)
300
+ end
301
+
302
+ it 'preserves a false value on later fetches' do
303
+ subject.fetch(:sub_a, false)
304
+
305
+ expect(subject.fetch(:sub_a, true) { :unused }).to be(false)
306
+ end
307
+
308
+ it 'uses string keys and evaluates a block only for an empty value' do
309
+ expect(subject.fetch('sub_a') { |key| "#{key} default" }).to eq('sub_a default')
310
+ subject.sub_a = 'changed'
311
+
312
+ expect(subject.fetch('sub_a') { |key| raise "unexpected default for #{key}" }).to eq('changed')
313
+ end
280
314
  end
281
315
 
282
316
  context 'when the fetched key has a value' do
@@ -76,4 +76,10 @@ RSpec.describe Faraday::ProxyOptions do
76
76
  expect(proxy.password).to eq('pw d')
77
77
  expect(proxy[:password]).to eq('pw d')
78
78
  end
79
+
80
+ it 'preserves literal plus signs in userinfo' do
81
+ proxy = Faraday::ProxyOptions.from 'http://a+b:pw+d%2B@example.org'
82
+ expect(proxy.user).to eq('a+b')
83
+ expect(proxy.password).to eq('pw+d+')
84
+ end
79
85
  end
@@ -67,6 +67,12 @@ RSpec.describe Faraday::RackBuilder do
67
67
  expect { subject.use(Orange) }.to raise_error(Faraday::RackBuilder::StackLocked)
68
68
  end
69
69
 
70
+ it 'rejects adapter changes after locking the stack' do
71
+ conn.get('/')
72
+ expect { subject.adapter(:net_http) }.to raise_error(Faraday::RackBuilder::StackLocked)
73
+ expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(:test))
74
+ end
75
+
70
76
  it 'dup stack is unlocked' do
71
77
  expect(subject.locked?).to be_falsey
72
78
  subject.lock!
data/spec/faraday_spec.rb CHANGED
@@ -40,4 +40,30 @@ RSpec.describe Faraday do
40
40
  Faraday.default_connection = nil
41
41
  end
42
42
  end
43
+
44
+ context 'HTTP verb methods' do
45
+ let(:mock_connection) { double('Connection') }
46
+
47
+ before do
48
+ Faraday.default_connection = mock_connection
49
+ end
50
+
51
+ (Faraday::METHODS_WITH_QUERY + Faraday::METHODS_WITH_BODY).each do |http_method|
52
+ it "defines .#{http_method} and forwards it to the default_connection" do
53
+ expect(Faraday.singleton_class.instance_methods(false)).to include(http_method.to_sym)
54
+
55
+ block = proc { |request| request }
56
+ expect(mock_connection).to receive(http_method) do |*args, &blk|
57
+ expect(args).to eq(['/foo', { page: 1 }, { 'X-Custom' => 'yes' }])
58
+ expect(blk).to be(block)
59
+ end
60
+
61
+ Faraday.public_send(http_method, '/foo', { page: 1 }, { 'X-Custom' => 'yes' }, &block)
62
+ end
63
+ end
64
+
65
+ after do
66
+ Faraday.default_connection = nil
67
+ end
68
+ end
43
69
  end
data/spec/spec_helper.rb CHANGED
@@ -24,9 +24,18 @@ WebMock.disable_net_connect!(allow_localhost: true)
24
24
  SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter]
25
25
 
26
26
  SimpleCov.start do
27
- add_filter '/spec/'
28
27
  minimum_coverage 84
29
- minimum_coverage_by_file 26
28
+
29
+ # SimpleCov 1.0 first, then older.
30
+ if SimpleCov.respond_to?(:skip)
31
+ skip '/spec/'
32
+ skip '/tmp/'
33
+ coverage(:line) { minimum 26, per: :file }
34
+ else
35
+ add_filter '/spec/'
36
+ add_filter '/tmp/'
37
+ minimum_coverage_by_file 26
38
+ end
30
39
  end
31
40
 
32
41
  require 'faraday'
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.14.3
4
+ version: 2.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "@technoweenie"
@@ -20,7 +20,7 @@ dependencies:
20
20
  version: '2.0'
21
21
  - - "<"
22
22
  - !ruby/object:Gem::Version
23
- version: '3.5'
23
+ version: '3.6'
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
@@ -30,7 +30,7 @@ dependencies:
30
30
  version: '2.0'
31
31
  - - "<"
32
32
  - !ruby/object:Gem::Version
33
- version: '3.5'
33
+ version: '3.6'
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: json
36
36
  requirement: !ruby/object:Gem::Requirement
@@ -144,7 +144,7 @@ licenses:
144
144
  - MIT
145
145
  metadata:
146
146
  homepage_uri: https://lostisland.github.io/faraday
147
- changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.14.3
147
+ changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.15.0
148
148
  source_code_uri: https://github.com/lostisland/faraday
149
149
  bug_tracker_uri: https://github.com/lostisland/faraday/issues
150
150
  rubygems_mfa_required: 'true'