faraday 1.10.4 → 2.13.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +198 -4
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -20
  5. data/Rakefile +6 -1
  6. data/examples/client_spec.rb +41 -19
  7. data/examples/client_test.rb +48 -22
  8. data/lib/faraday/adapter/test.rb +62 -13
  9. data/lib/faraday/adapter.rb +6 -10
  10. data/lib/faraday/connection.rb +72 -150
  11. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  12. data/lib/faraday/error.rb +24 -5
  13. data/lib/faraday/logging/formatter.rb +29 -16
  14. data/lib/faraday/middleware.rb +43 -2
  15. data/lib/faraday/middleware_registry.rb +17 -63
  16. data/lib/faraday/options/connection_options.rb +7 -6
  17. data/lib/faraday/options/env.rb +85 -62
  18. data/lib/faraday/options/proxy_options.rb +11 -5
  19. data/lib/faraday/options/request_options.rb +7 -6
  20. data/lib/faraday/options/ssl_options.rb +62 -45
  21. data/lib/faraday/options.rb +7 -6
  22. data/lib/faraday/rack_builder.rb +43 -40
  23. data/lib/faraday/request/authorization.rb +33 -41
  24. data/lib/faraday/request/instrumentation.rb +5 -1
  25. data/lib/faraday/request/json.rb +18 -3
  26. data/lib/faraday/request/url_encoded.rb +5 -1
  27. data/lib/faraday/request.rb +15 -30
  28. data/lib/faraday/response/json.rb +25 -5
  29. data/lib/faraday/response/logger.rb +11 -3
  30. data/lib/faraday/response/raise_error.rb +45 -18
  31. data/lib/faraday/response.rb +9 -21
  32. data/lib/faraday/utils/headers.rb +15 -4
  33. data/lib/faraday/utils.rb +11 -7
  34. data/lib/faraday/version.rb +1 -1
  35. data/lib/faraday.rb +8 -44
  36. data/spec/faraday/adapter/test_spec.rb +65 -0
  37. data/spec/faraday/connection_spec.rb +165 -93
  38. data/spec/faraday/error_spec.rb +39 -6
  39. data/spec/faraday/middleware_registry_spec.rb +31 -0
  40. data/spec/faraday/middleware_spec.rb +161 -0
  41. data/spec/faraday/options/env_spec.rb +8 -2
  42. data/spec/faraday/options/options_spec.rb +1 -1
  43. data/spec/faraday/options/proxy_options_spec.rb +35 -0
  44. data/spec/faraday/params_encoders/nested_spec.rb +10 -1
  45. data/spec/faraday/rack_builder_spec.rb +26 -54
  46. data/spec/faraday/request/authorization_spec.rb +50 -28
  47. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  48. data/spec/faraday/request/json_spec.rb +88 -0
  49. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  50. data/spec/faraday/request_spec.rb +5 -15
  51. data/spec/faraday/response/json_spec.rb +93 -6
  52. data/spec/faraday/response/logger_spec.rb +77 -4
  53. data/spec/faraday/response/raise_error_spec.rb +111 -5
  54. data/spec/faraday/response_spec.rb +3 -1
  55. data/spec/faraday/utils/headers_spec.rb +31 -4
  56. data/spec/faraday/utils_spec.rb +65 -1
  57. data/spec/faraday_spec.rb +10 -4
  58. data/spec/spec_helper.rb +5 -6
  59. data/spec/support/fake_safe_buffer.rb +1 -1
  60. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  61. data/spec/support/helper_methods.rb +0 -37
  62. data/spec/support/shared_examples/adapter.rb +2 -2
  63. data/spec/support/shared_examples/request_method.rb +22 -21
  64. metadata +24 -149
  65. data/lib/faraday/adapter/typhoeus.rb +0 -15
  66. data/lib/faraday/autoload.rb +0 -89
  67. data/lib/faraday/dependency_loader.rb +0 -39
  68. data/lib/faraday/deprecate.rb +0 -110
  69. data/lib/faraday/request/basic_authentication.rb +0 -20
  70. data/lib/faraday/request/token_authentication.rb +0 -20
  71. data/spec/faraday/adapter/em_http_spec.rb +0 -49
  72. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
  73. data/spec/faraday/adapter/excon_spec.rb +0 -49
  74. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  75. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  76. data/spec/faraday/adapter/patron_spec.rb +0 -18
  77. data/spec/faraday/adapter/rack_spec.rb +0 -8
  78. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  79. data/spec/faraday/composite_read_io_spec.rb +0 -80
  80. data/spec/faraday/deprecate_spec.rb +0 -147
  81. data/spec/faraday/response/middleware_spec.rb +0 -68
  82. data/spec/support/webmock_rack_app.rb +0 -68
@@ -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
 
@@ -55,6 +72,7 @@ module Faraday
55
72
  @stack = {}
56
73
  @consumed = {}
57
74
  @strict_mode = strict_mode
75
+ @stubs_mutex = Monitor.new
58
76
  yield(self) if block_given?
59
77
  end
60
78
 
@@ -70,10 +88,13 @@ module Faraday
70
88
  stack = @stack[request_method]
71
89
  consumed = (@consumed[request_method] ||= [])
72
90
 
73
- stub, meta = matches?(stack, env)
74
- if stub
75
- consumed << stack.delete(stub)
76
- return stub, meta
91
+ @stubs_mutex.synchronize do
92
+ stub, meta = matches?(stack, env)
93
+ if stub
94
+ removed = stack.delete(stub)
95
+ consumed << removed unless removed.nil?
96
+ return stub, meta
97
+ end
77
98
  end
78
99
  matches?(consumed, env)
79
100
  end
@@ -125,7 +146,7 @@ module Faraday
125
146
  # which means that all of a path, parameters, and headers must be the same as an actual request.
126
147
  def strict_mode=(value)
127
148
  @strict_mode = value
128
- @stack.each do |_method, stubs|
149
+ @stack.each_value do |stubs|
129
150
  stubs.each do |stub|
130
151
  stub.strict_mode = value
131
152
  end
@@ -163,7 +184,7 @@ module Faraday
163
184
  end
164
185
 
165
186
  # Stub request
166
- class Stub < Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) # rubocop:disable Style/StructInheritance
187
+ Stub = Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) do
167
188
  # @param env [Faraday::Env]
168
189
  def matches?(env)
169
190
  request_host = env[:url].host
@@ -177,7 +198,7 @@ module Faraday
177
198
  [(host.nil? || host == request_host) &&
178
199
  path_match?(request_path, meta) &&
179
200
  params_match?(env) &&
180
- (body.to_s.size.zero? || request_body == body) &&
201
+ body_match?(request_body) &&
181
202
  headers_match?(request_headers), meta]
182
203
  end
183
204
 
@@ -218,6 +239,17 @@ module Faraday
218
239
  end
219
240
  end
220
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
+
221
253
  def to_s
222
254
  "#{path} #{body}"
223
255
  end
@@ -242,21 +274,38 @@ module Faraday
242
274
  stub, meta = stubs.match(env)
243
275
 
244
276
  unless stub
245
- raise Stubs::NotFound, "no stubbed request for #{env[:method]} "\
246
- "#{env[:url]} #{env[:body]}"
277
+ raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
278
+ "#{env[:url]} #{env[:body]} #{env[:headers]}"
247
279
  end
248
280
 
249
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
+
250
291
  status, headers, body =
251
- if block_arity >= 0
252
- 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
253
296
  else
254
- stub.block.call(env, meta)
297
+ stub.block.call(*params)
255
298
  end
256
- 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)
257
304
 
258
305
  @app.call(env)
259
306
  end
260
307
  end
261
308
  end
262
309
  end
310
+
311
+ Faraday::Adapter.register_middleware(test: Faraday::Adapter::Test)
@@ -5,14 +5,9 @@ module Faraday
5
5
  # responsible for fulfilling a Faraday request.
6
6
  class Adapter
7
7
  extend MiddlewareRegistry
8
- extend DependencyLoader
9
8
 
10
9
  CONTENT_LENGTH = 'Content-Length'
11
10
 
12
- register_middleware File.expand_path('adapter', __dir__),
13
- test: [:Test, 'test'],
14
- typhoeus: [:Typhoeus, 'typhoeus']
15
-
16
11
  # This module marks an Adapter as supporting parallel requests.
17
12
  module Parallelism
18
13
  attr_writer :supports_parallel
@@ -31,7 +26,7 @@ module Faraday
31
26
  self.supports_parallel = false
32
27
 
33
28
  def initialize(_app = nil, opts = {}, &block)
34
- @app = ->(env) { env.response }
29
+ @app = lambda(&:response)
35
30
  @connection_options = opts
36
31
  @config_block = block
37
32
  end
@@ -64,7 +59,7 @@ module Faraday
64
59
 
65
60
  private
66
61
 
67
- 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)
68
63
  env.status = status
69
64
  env.body = body
70
65
  env.reason_phrase = reason_phrase&.to_s&.strip
@@ -73,7 +68,7 @@ module Faraday
73
68
  yield(response_headers) if block_given?
74
69
  end
75
70
 
76
- env.response.finish(env) unless env.parallel?
71
+ env.response.finish(env) unless env.parallel? || !finished
77
72
  env.response
78
73
  end
79
74
 
@@ -83,8 +78,7 @@ module Faraday
83
78
  # @param type [Symbol] Describes which timeout setting to get: :read,
84
79
  # :write, or :open.
85
80
  # @param options [Hash] Hash containing Symbol keys like :timeout,
86
- # :read_timeout, :write_timeout, :open_timeout, or
87
- # :timeout
81
+ # :read_timeout, :write_timeout, or :open_timeout
88
82
  #
89
83
  # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
90
84
  # has been set.
@@ -103,3 +97,5 @@ module Faraday
103
97
  }.freeze
104
98
  end
105
99
  end
100
+
101
+ require 'faraday/adapter/test'
@@ -1,23 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faraday/deprecate'
4
-
5
3
  module Faraday
6
4
  # Connection objects manage the default properties and the middleware
7
5
  # stack for fulfilling an HTTP request.
8
6
  #
9
7
  # @example
10
8
  #
11
- # conn = Faraday::Connection.new 'http://sushi.com'
9
+ # conn = Faraday::Connection.new 'http://httpbingo.org'
12
10
  #
13
- # # GET http://sushi.com/nigiri
11
+ # # GET http://httpbingo.org/nigiri
14
12
  # conn.get 'nigiri'
15
13
  # # => #<Faraday::Response>
16
14
  #
17
15
  class Connection
18
16
  # A Set of allowed HTTP verbs.
19
17
  METHODS = Set.new %i[get post put delete head patch options trace]
20
- USER_AGENT = "Faraday v#{VERSION}"
18
+ USER_AGENT = "Faraday v#{VERSION}".freeze
21
19
 
22
20
  # @return [Hash] URI query unencoded key/value pairs.
23
21
  attr_reader :params
@@ -66,7 +64,7 @@ module Faraday
66
64
  options = ConnectionOptions.from(options)
67
65
 
68
66
  if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
69
- options = options.merge(url)
67
+ options = Utils.deep_merge(options, url)
70
68
  url = options.url
71
69
  end
72
70
 
@@ -119,7 +117,7 @@ module Faraday
119
117
 
120
118
  extend Forwardable
121
119
 
122
- def_delegators :builder, :build, :use, :request, :response, :adapter, :app
120
+ def_delegators :builder, :use, :request, :response, :adapter, :app
123
121
 
124
122
  # Closes the underlying resources and/or connections. In the case of
125
123
  # persistent connections, this closes all currently open connections
@@ -132,10 +130,10 @@ module Faraday
132
130
  # Makes a GET HTTP request without a body.
133
131
  # @!scope class
134
132
  #
135
- # @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
136
134
  # all requests. Can also be the options Hash.
137
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
138
- # @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.
139
137
  #
140
138
  # @example
141
139
  # conn.get '/items', { page: 1 }, :accept => 'application/json'
@@ -154,10 +152,10 @@ module Faraday
154
152
  # Makes a HEAD HTTP request without a body.
155
153
  # @!scope class
156
154
  #
157
- # @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
158
156
  # all requests. Can also be the options Hash.
159
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
160
- # @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.
161
159
  #
162
160
  # @example
163
161
  # conn.head '/items/1'
@@ -169,10 +167,10 @@ module Faraday
169
167
  # Makes a DELETE HTTP request without a body.
170
168
  # @!scope class
171
169
  #
172
- # @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
173
171
  # all requests. Can also be the options Hash.
174
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
175
- # @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.
176
174
  #
177
175
  # @example
178
176
  # conn.delete '/items/1'
@@ -184,10 +182,10 @@ module Faraday
184
182
  # Makes a TRACE HTTP request without a body.
185
183
  # @!scope class
186
184
  #
187
- # @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
188
186
  # all requests. Can also be the options Hash.
189
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
190
- # @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.
191
189
  #
192
190
  # @example
193
191
  # conn.connect '/items/1'
@@ -212,9 +210,9 @@ module Faraday
212
210
  #
213
211
  # @overload options(url, params = nil, headers = nil)
214
212
  # Makes an OPTIONS HTTP request to the given URL.
215
- # @param url [String] String base URL to sue as a prefix for all requests.
216
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
217
- # @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.
218
216
  #
219
217
  # @example
220
218
  # conn.options '/items/1'
@@ -222,7 +220,7 @@ module Faraday
222
220
  # @yield [Faraday::Request] for further request customizations
223
221
  # @return [Faraday::Response]
224
222
  def options(*args)
225
- return @options if args.size.zero?
223
+ return @options if args.empty?
226
224
 
227
225
  url, params, headers = *args
228
226
  run_request(:options, url, nil, headers) do |request|
@@ -235,10 +233,10 @@ module Faraday
235
233
  # Makes a POST HTTP request with a body.
236
234
  # @!scope class
237
235
  #
238
- # @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
239
237
  # all requests. Can also be the options Hash.
240
- # @param body [String] body for the request.
241
- # @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.
242
240
  #
243
241
  # @example
244
242
  # conn.post '/items', data, content_type: 'application/json'
@@ -257,20 +255,19 @@ module Faraday
257
255
  # Makes a PUT HTTP request with a body.
258
256
  # @!scope class
259
257
  #
260
- # @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
261
259
  # all requests. Can also be the options Hash.
262
- # @param body [String] body for the request.
263
- # @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.
264
262
  #
265
263
  # @example
266
- # # TODO: Make it a PUT example
267
- # conn.post '/items', data, content_type: 'application/json'
264
+ # conn.put '/products/123', data, content_type: 'application/json'
268
265
  #
269
- # # Simple ElasticSearch indexing sample.
270
- # conn.post '/twitter/tweet' do |req|
271
- # req.headers[:content_type] = 'application/json'
272
- # req.params[:routing] = 'kimchy'
273
- # 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'
274
271
  # end
275
272
  #
276
273
  # @yield [Faraday::Request] for further request customizations
@@ -285,75 +282,6 @@ module Faraday
285
282
  RUBY
286
283
  end
287
284
 
288
- # Sets up the Authorization header with these credentials, encoded
289
- # with base64.
290
- #
291
- # @param login [String] The authentication login.
292
- # @param pass [String] The authentication password.
293
- #
294
- # @example
295
- #
296
- # conn.basic_auth 'Aladdin', 'open sesame'
297
- # conn.headers['Authorization']
298
- # # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
299
- #
300
- # @return [void]
301
- def basic_auth(login, pass)
302
- set_authorization_header(:basic_auth, login, pass)
303
- end
304
-
305
- extend Faraday::Deprecate
306
- deprecate :basic_auth, '#request(:basic_auth, ...)', '2.0'
307
-
308
- # Sets up the Authorization header with the given token.
309
- #
310
- # @param token [String]
311
- # @param options [Hash] extra token options.
312
- #
313
- # @example
314
- #
315
- # conn.token_auth 'abcdef', foo: 'bar'
316
- # conn.headers['Authorization']
317
- # # => "Token token=\"abcdef\",
318
- # foo=\"bar\""
319
- #
320
- # @return [void]
321
- def token_auth(token, options = nil)
322
- set_authorization_header(:token_auth, token, options)
323
- end
324
-
325
- deprecate :token_auth,
326
- '#request(:token_auth, ...)',
327
- '2.0',
328
- 'See https://lostisland.github.io/faraday/middleware/authentication for more usage info.'
329
-
330
- # Sets up a custom Authorization header.
331
- #
332
- # @param type [String] authorization type
333
- # @param token [String, Hash] token. A String value is taken literally, and
334
- # a Hash is encoded into comma-separated key/value pairs.
335
- #
336
- # @example
337
- #
338
- # conn.authorization :Bearer, 'mF_9.B5f-4.1JqM'
339
- # conn.headers['Authorization']
340
- # # => "Bearer mF_9.B5f-4.1JqM"
341
- #
342
- # conn.authorization :Token, token: 'abcdef', foo: 'bar'
343
- # conn.headers['Authorization']
344
- # # => "Token token=\"abcdef\",
345
- # foo=\"bar\""
346
- #
347
- # @return [void]
348
- def authorization(type, token)
349
- set_authorization_header(:authorization, type, token)
350
- end
351
-
352
- deprecate :authorization,
353
- '#request(:authorization, ...)',
354
- '2.0',
355
- 'See https://lostisland.github.io/faraday/middleware/authentication for more usage info.'
356
-
357
285
  # Check if the adapter is parallel-capable.
358
286
  #
359
287
  # @yield if the adapter isn't parallel-capable, or if no adapter is set yet.
@@ -386,15 +314,23 @@ module Faraday
386
314
  #
387
315
  # @yield a block to execute multiple requests.
388
316
  # @return [void]
389
- def in_parallel(manager = nil)
317
+ def in_parallel(manager = nil, &block)
390
318
  @parallel_manager = manager || default_parallel_manager do
391
319
  warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
392
320
  'on Faraday stack'
393
321
  warn caller[2, 10].join("\n")
394
322
  nil
395
323
  end
396
- yield
397
- @parallel_manager&.run
324
+ return yield unless @parallel_manager
325
+
326
+ if @parallel_manager.respond_to?(:execute)
327
+ # Execute is the new method that is responsible for executing the block.
328
+ @parallel_manager.execute(&block)
329
+ else
330
+ # TODO: Old behaviour, deprecate and remove in 3.0
331
+ yield
332
+ @parallel_manager.run
333
+ end
398
334
  ensure
399
335
  @parallel_manager = nil
400
336
  end
@@ -420,11 +356,11 @@ module Faraday
420
356
  # @example
421
357
  #
422
358
  # conn = Faraday::Connection.new { ... }
423
- # conn.url_prefix = "https://sushi.com/api"
359
+ # conn.url_prefix = "https://httpbingo.org/api"
424
360
  # conn.scheme # => https
425
361
  # conn.path_prefix # => "/api"
426
362
  #
427
- # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
363
+ # conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri
428
364
  def url_prefix=(url, encoder = nil)
429
365
  uri = @url_prefix = Utils.URI(url)
430
366
  self.path_prefix = uri.path
@@ -441,7 +377,7 @@ module Faraday
441
377
  end
442
378
 
443
379
  def set_basic_auth(user, password)
444
- header = Faraday::Request::BasicAuthentication.header(user, password)
380
+ header = Faraday::Utils.basic_header_from(user, password)
445
381
  headers[Faraday::Request::Authorization::KEY] = header
446
382
  end
447
383
 
@@ -461,20 +397,20 @@ module Faraday
461
397
  # Takes a relative url for a request and combines it with the defaults
462
398
  # set on the connection instance.
463
399
  #
464
- # @param url [String]
400
+ # @param url [String, URI, nil]
465
401
  # @param extra_params [Hash]
466
402
  #
467
403
  # @example
468
404
  # conn = Faraday::Connection.new { ... }
469
- # conn.url_prefix = "https://sushi.com/api?token=abc"
405
+ # conn.url_prefix = "https://httpbingo.org/api?token=abc"
470
406
  # conn.scheme # => https
471
407
  # conn.path_prefix # => "/api"
472
408
  #
473
409
  # conn.build_url("nigiri?page=2")
474
- # # => https://sushi.com/api/nigiri?token=abc&page=2
410
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
475
411
  #
476
412
  # conn.build_url("nigiri", page: 2)
477
- # # => https://sushi.com/api/nigiri?token=abc&page=2
413
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
478
414
  #
479
415
  def build_url(url = nil, extra_params = nil)
480
416
  uri = build_exclusive_url(url)
@@ -494,10 +430,10 @@ module Faraday
494
430
  # Builds and runs the Faraday::Request.
495
431
  #
496
432
  # @param method [Symbol] HTTP method.
497
- # @param url [String, URI] String or URI to access.
498
- # @param body [Object] The request body that will eventually be converted to
499
- # a string.
500
- # @param headers [Hash] unencoded HTTP header key/value pairs.
433
+ # @param url [String, URI, nil] String or URI to access.
434
+ # @param body [String, Hash, Array, nil] The request body that will eventually be converted to
435
+ # a string; middlewares can be used to support more complex types.
436
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
501
437
  #
502
438
  # @return [Faraday::Response]
503
439
  def run_request(method, url, body, headers)
@@ -533,7 +469,7 @@ module Faraday
533
469
 
534
470
  # Build an absolute URL based on url_prefix.
535
471
  #
536
- # @param url [String, URI]
472
+ # @param url [String, URI, nil]
537
473
  # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
538
474
  # replace the query values
539
475
  # of the resulting url (default: nil).
@@ -542,17 +478,16 @@ module Faraday
542
478
  def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
543
479
  url = nil if url.respond_to?(:empty?) && url.empty?
544
480
  base = url_prefix.dup
545
- if url && base.path && base.path !~ %r{/$}
481
+ if url && !base.path.end_with?('/')
546
482
  base.path = "#{base.path}/" # ensure trailing slash
547
483
  end
548
- url = url && URI.parse(url.to_s).opaque ? url.to_s.gsub(':', '%3A') : url
484
+ # Ensure relative url will be parsed correctly (such as `service:search` )
485
+ url = "./#{url}" if url.respond_to?(:start_with?) && !url.start_with?('http://', 'https://', '/', './', '../')
549
486
  uri = url ? base + url : base
550
487
  if params
551
488
  uri.query = params.to_query(params_encoder || options.params_encoder)
552
489
  end
553
- # rubocop:disable Style/SafeNavigation
554
490
  uri.query = nil if uri.query && uri.query.empty?
555
- # rubocop:enable Style/SafeNavigation
556
491
  uri
557
492
  end
558
493
 
@@ -584,41 +519,28 @@ module Faraday
584
519
  yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
585
520
  end
586
521
 
587
- def set_authorization_header(header_type, *args)
588
- header = Faraday::Request
589
- .lookup_middleware(header_type)
590
- .header(*args)
591
-
592
- headers[Faraday::Request::Authorization::KEY] = header
593
- end
594
-
595
522
  def proxy_from_env(url)
596
523
  return if Faraday.ignore_env_proxy
597
524
 
598
525
  uri = nil
599
- if URI.parse('').respond_to?(:find_proxy)
600
- case url
601
- when String
602
- uri = Utils.URI(url)
603
- uri = if uri.host.nil?
604
- find_default_proxy
605
- else
606
- URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
607
- end
608
- when URI
609
- uri = url.find_proxy
610
- when nil
611
- uri = find_default_proxy
612
- end
613
- else
614
- warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
526
+ case url
527
+ when String
528
+ uri = Utils.URI(url)
529
+ uri = if uri.host.nil?
530
+ find_default_proxy
531
+ else
532
+ URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
533
+ end
534
+ when URI
535
+ uri = url.find_proxy
536
+ when nil
615
537
  uri = find_default_proxy
616
538
  end
617
539
  ProxyOptions.from(uri) if uri
618
540
  end
619
541
 
620
542
  def find_default_proxy
621
- uri = ENV['http_proxy']
543
+ uri = ENV.fetch('http_proxy', nil)
622
544
  return unless uri && !uri.empty?
623
545
 
624
546
  uri = "http://#{uri}" unless uri.match?(/^http/i)
@@ -636,7 +558,7 @@ module Faraday
636
558
  end
637
559
 
638
560
  def support_parallel?(adapter)
639
- adapter&.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
561
+ adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
640
562
  end
641
563
  end
642
564
  end
@@ -62,11 +62,17 @@ module Faraday
62
62
  end
63
63
 
64
64
  def encode_array(parent, value)
65
- new_parent = "#{parent}%5B%5D"
66
- return new_parent if value.empty?
65
+ return "#{parent}%5B%5D" if value.empty?
67
66
 
68
67
  buffer = +''
69
- value.each { |val| buffer << "#{encode_pair(new_parent, val)}&" }
68
+ value.each_with_index do |val, index|
69
+ new_parent = if @array_indices
70
+ "#{parent}%5B#{index}%5D"
71
+ else
72
+ "#{parent}%5B%5D"
73
+ end
74
+ buffer << "#{encode_pair(new_parent, val)}&"
75
+ end
70
76
  buffer.chop
71
77
  end
72
78
  end
@@ -96,13 +102,13 @@ module Faraday
96
102
 
97
103
  protected
98
104
 
99
- SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/.freeze
105
+ SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/
100
106
 
101
107
  def decode_pair(key, value, context)
102
108
  subkeys = key.scan(SUBKEYS_REGEX)
103
109
  subkeys.each_with_index do |subkey, i|
104
110
  is_array = subkey =~ /[\[\]]+\Z/
105
- subkey = $` if is_array
111
+ subkey = Regexp.last_match.pre_match if is_array
106
112
  last_subkey = i == subkeys.length - 1
107
113
 
108
114
  context = prepare_context(context, subkey, is_array, last_subkey)
@@ -124,7 +130,7 @@ module Faraday
124
130
  value_type = is_array ? Array : Hash
125
131
  if context[subkey] && !context[subkey].is_a?(value_type)
126
132
  raise TypeError, "expected #{value_type.name} " \
127
- "(got #{context[subkey].class.name}) for param `#{subkey}'"
133
+ "(got #{context[subkey].class.name}) for param `#{subkey}'"
128
134
  end
129
135
 
130
136
  context[subkey] ||= value_type.new
@@ -161,7 +167,7 @@ module Faraday
161
167
  # for your requests.
162
168
  module NestedParamsEncoder
163
169
  class << self
164
- attr_accessor :sort_params
170
+ attr_accessor :sort_params, :array_indices
165
171
 
166
172
  extend Forwardable
167
173
  def_delegators :'Faraday::Utils', :escape, :unescape
@@ -169,6 +175,7 @@ module Faraday
169
175
 
170
176
  # Useful default for OAuth and caching.
171
177
  @sort_params = true
178
+ @array_indices = false
172
179
 
173
180
  extend EncodeMethods
174
181
  extend DecodeMethods