faraday 1.10.3 → 2.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -3
  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 +61 -147
  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 +28 -15
  14. data/lib/faraday/middleware.rb +46 -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 +7 -3
  19. data/lib/faraday/options/request_options.rb +7 -6
  20. data/lib/faraday/options/ssl_options.rb +56 -45
  21. data/lib/faraday/options.rb +7 -6
  22. data/lib/faraday/rack_builder.rb +23 -21
  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 +6 -0
  30. data/lib/faraday/response/raise_error.rb +35 -6
  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 +31 -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 +8 -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 +38 -0
  53. data/spec/faraday/response/raise_error_spec.rb +91 -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 +63 -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 +18 -154
  65. data/lib/faraday/adapter/typhoeus.rb +0 -15
  66. data/lib/faraday/autoload.rb +0 -87
  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.
@@ -420,11 +348,11 @@ module Faraday
420
348
  # @example
421
349
  #
422
350
  # conn = Faraday::Connection.new { ... }
423
- # conn.url_prefix = "https://sushi.com/api"
351
+ # conn.url_prefix = "https://httpbingo.org/api"
424
352
  # conn.scheme # => https
425
353
  # conn.path_prefix # => "/api"
426
354
  #
427
- # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
355
+ # conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri
428
356
  def url_prefix=(url, encoder = nil)
429
357
  uri = @url_prefix = Utils.URI(url)
430
358
  self.path_prefix = uri.path
@@ -441,7 +369,7 @@ module Faraday
441
369
  end
442
370
 
443
371
  def set_basic_auth(user, password)
444
- header = Faraday::Request::BasicAuthentication.header(user, password)
372
+ header = Faraday::Utils.basic_header_from(user, password)
445
373
  headers[Faraday::Request::Authorization::KEY] = header
446
374
  end
447
375
 
@@ -461,20 +389,20 @@ module Faraday
461
389
  # Takes a relative url for a request and combines it with the defaults
462
390
  # set on the connection instance.
463
391
  #
464
- # @param url [String]
392
+ # @param url [String, URI, nil]
465
393
  # @param extra_params [Hash]
466
394
  #
467
395
  # @example
468
396
  # conn = Faraday::Connection.new { ... }
469
- # conn.url_prefix = "https://sushi.com/api?token=abc"
397
+ # conn.url_prefix = "https://httpbingo.org/api?token=abc"
470
398
  # conn.scheme # => https
471
399
  # conn.path_prefix # => "/api"
472
400
  #
473
401
  # conn.build_url("nigiri?page=2")
474
- # # => https://sushi.com/api/nigiri?token=abc&page=2
402
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
475
403
  #
476
404
  # conn.build_url("nigiri", page: 2)
477
- # # => https://sushi.com/api/nigiri?token=abc&page=2
405
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
478
406
  #
479
407
  def build_url(url = nil, extra_params = nil)
480
408
  uri = build_exclusive_url(url)
@@ -494,10 +422,10 @@ module Faraday
494
422
  # Builds and runs the Faraday::Request.
495
423
  #
496
424
  # @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.
425
+ # @param url [String, URI, nil] String or URI to access.
426
+ # @param body [String, Hash, Array, nil] The request body that will eventually be converted to
427
+ # a string; middlewares can be used to support more complex types.
428
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
501
429
  #
502
430
  # @return [Faraday::Response]
503
431
  def run_request(method, url, body, headers)
@@ -533,7 +461,7 @@ module Faraday
533
461
 
534
462
  # Build an absolute URL based on url_prefix.
535
463
  #
536
- # @param url [String, URI]
464
+ # @param url [String, URI, nil]
537
465
  # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
538
466
  # replace the query values
539
467
  # of the resulting url (default: nil).
@@ -542,17 +470,16 @@ module Faraday
542
470
  def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
543
471
  url = nil if url.respond_to?(:empty?) && url.empty?
544
472
  base = url_prefix.dup
545
- if url && base.path && base.path !~ %r{/$}
473
+ if url && !base.path.end_with?('/')
546
474
  base.path = "#{base.path}/" # ensure trailing slash
547
475
  end
548
- url = url && URI.parse(url.to_s).opaque ? url.to_s.gsub(':', '%3A') : url
476
+ # Ensure relative url will be parsed correctly (such as `service:search` )
477
+ url = "./#{url}" if url.respond_to?(:start_with?) && !url.start_with?('http://', 'https://', '/', './', '../')
549
478
  uri = url ? base + url : base
550
479
  if params
551
480
  uri.query = params.to_query(params_encoder || options.params_encoder)
552
481
  end
553
- # rubocop:disable Style/SafeNavigation
554
482
  uri.query = nil if uri.query && uri.query.empty?
555
- # rubocop:enable Style/SafeNavigation
556
483
  uri
557
484
  end
558
485
 
@@ -584,41 +511,28 @@ module Faraday
584
511
  yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
585
512
  end
586
513
 
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
514
  def proxy_from_env(url)
596
515
  return if Faraday.ignore_env_proxy
597
516
 
598
517
  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']
518
+ case url
519
+ when String
520
+ uri = Utils.URI(url)
521
+ uri = if uri.host.nil?
522
+ find_default_proxy
523
+ else
524
+ URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
525
+ end
526
+ when URI
527
+ uri = url.find_proxy
528
+ when nil
615
529
  uri = find_default_proxy
616
530
  end
617
531
  ProxyOptions.from(uri) if uri
618
532
  end
619
533
 
620
534
  def find_default_proxy
621
- uri = ENV['http_proxy']
535
+ uri = ENV.fetch('http_proxy', nil)
622
536
  return unless uri && !uri.empty?
623
537
 
624
538
  uri = "http://#{uri}" unless uri.match?(/^http/i)
@@ -636,7 +550,7 @@ module Faraday
636
550
  end
637
551
 
638
552
  def support_parallel?(adapter)
639
- adapter&.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
553
+ adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
640
554
  end
641
555
  end
642
556
  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