faraday 1.10.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -3
  3. data/LICENSE.md +1 -1
  4. data/README.md +18 -16
  5. data/examples/client_spec.rb +41 -19
  6. data/examples/client_test.rb +48 -22
  7. data/lib/faraday/adapter/test.rb +59 -10
  8. data/lib/faraday/adapter.rb +5 -9
  9. data/lib/faraday/connection.rb +47 -129
  10. data/lib/faraday/encoders/nested_params_encoder.rb +13 -6
  11. data/lib/faraday/error.rb +3 -2
  12. data/lib/faraday/logging/formatter.rb +19 -2
  13. data/lib/faraday/middleware.rb +3 -1
  14. data/lib/faraday/middleware_registry.rb +17 -63
  15. data/lib/faraday/options/env.rb +31 -7
  16. data/lib/faraday/options/ssl_options.rb +11 -1
  17. data/lib/faraday/options.rb +3 -3
  18. data/lib/faraday/rack_builder.rb +23 -20
  19. data/lib/faraday/request/authorization.rb +33 -41
  20. data/lib/faraday/request/instrumentation.rb +2 -0
  21. data/lib/faraday/request/url_encoded.rb +5 -1
  22. data/lib/faraday/request.rb +7 -26
  23. data/lib/faraday/response/json.rb +4 -4
  24. data/lib/faraday/response/logger.rb +6 -0
  25. data/lib/faraday/response/raise_error.rb +9 -1
  26. data/lib/faraday/response.rb +9 -21
  27. data/lib/faraday/utils/headers.rb +7 -2
  28. data/lib/faraday/utils.rb +10 -5
  29. data/lib/faraday/version.rb +1 -1
  30. data/lib/faraday.rb +8 -44
  31. data/spec/faraday/adapter/test_spec.rb +65 -0
  32. data/spec/faraday/connection_spec.rb +163 -91
  33. data/spec/faraday/middleware_registry_spec.rb +31 -0
  34. data/spec/faraday/middleware_spec.rb +18 -0
  35. data/spec/faraday/options/env_spec.rb +8 -2
  36. data/spec/faraday/params_encoders/nested_spec.rb +8 -0
  37. data/spec/faraday/rack_builder_spec.rb +26 -54
  38. data/spec/faraday/request/authorization_spec.rb +50 -28
  39. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  40. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  41. data/spec/faraday/request_spec.rb +5 -15
  42. data/spec/faraday/response/json_spec.rb +4 -6
  43. data/spec/faraday/response/logger_spec.rb +28 -0
  44. data/spec/faraday/response/raise_error_spec.rb +7 -4
  45. data/spec/faraday/response_spec.rb +3 -1
  46. data/spec/faraday/utils/headers_spec.rb +22 -4
  47. data/spec/faraday/utils_spec.rb +63 -1
  48. data/spec/support/fake_safe_buffer.rb +1 -1
  49. data/spec/support/helper_methods.rb +0 -37
  50. data/spec/support/shared_examples/adapter.rb +2 -2
  51. data/spec/support/shared_examples/request_method.rb +22 -21
  52. metadata +14 -149
  53. data/lib/faraday/adapter/typhoeus.rb +0 -15
  54. data/lib/faraday/autoload.rb +0 -87
  55. data/lib/faraday/dependency_loader.rb +0 -37
  56. data/lib/faraday/request/basic_authentication.rb +0 -20
  57. data/lib/faraday/request/token_authentication.rb +0 -20
  58. data/spec/faraday/adapter/em_http_spec.rb +0 -49
  59. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -18
  60. data/spec/faraday/adapter/excon_spec.rb +0 -49
  61. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  62. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  63. data/spec/faraday/adapter/patron_spec.rb +0 -18
  64. data/spec/faraday/adapter/rack_spec.rb +0 -8
  65. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  66. data/spec/faraday/composite_read_io_spec.rb +0 -80
  67. data/spec/faraday/response/middleware_spec.rb +0 -68
  68. 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
@@ -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]} "\
277
+ raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
246
278
  "#{env[:url]} #{env[:body]}"
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
@@ -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'
@@ -6,9 +6,9 @@ module Faraday
6
6
  #
7
7
  # @example
8
8
  #
9
- # conn = Faraday::Connection.new 'http://sushi.com'
9
+ # conn = Faraday::Connection.new 'http://httpbingo.org'
10
10
  #
11
- # # GET http://sushi.com/nigiri
11
+ # # GET http://httpbingo.org/nigiri
12
12
  # conn.get 'nigiri'
13
13
  # # => #<Faraday::Response>
14
14
  #
@@ -64,7 +64,7 @@ module Faraday
64
64
  options = ConnectionOptions.from(options)
65
65
 
66
66
  if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
67
- options = options.merge(url)
67
+ options = Utils.deep_merge(options, url)
68
68
  url = options.url
69
69
  end
70
70
 
@@ -117,7 +117,7 @@ module Faraday
117
117
 
118
118
  extend Forwardable
119
119
 
120
- def_delegators :builder, :build, :use, :request, :response, :adapter, :app
120
+ def_delegators :builder, :use, :request, :response, :adapter, :app
121
121
 
122
122
  # Closes the underlying resources and/or connections. In the case of
123
123
  # persistent connections, this closes all currently open connections
@@ -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
@@ -283,77 +282,6 @@ module Faraday
283
282
  RUBY
284
283
  end
285
284
 
286
- # Sets up the Authorization header with these credentials, encoded
287
- # with base64.
288
- #
289
- # @param login [String] The authentication login.
290
- # @param pass [String] The authentication password.
291
- #
292
- # @example
293
- #
294
- # conn.basic_auth 'Aladdin', 'open sesame'
295
- # conn.headers['Authorization']
296
- # # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
297
- #
298
- # @return [void]
299
- def basic_auth(login, pass)
300
- warn <<~TEXT
301
- WARNING: `Faraday::Connection#basic_auth` is deprecated; it will be removed in version 2.0.
302
- While initializing your connection, use `#request(:basic_auth, ...)` instead.
303
- See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
304
- TEXT
305
- set_authorization_header(:basic_auth, login, pass)
306
- end
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
- warn <<~TEXT
323
- WARNING: `Faraday::Connection#token_auth` is deprecated; it will be removed in version 2.0.
324
- While initializing your connection, use `#request(:token_auth, ...)` instead.
325
- See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
326
- TEXT
327
- set_authorization_header(:token_auth, token, options)
328
- end
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
- warn <<~TEXT
350
- WARNING: `Faraday::Connection#authorization` is deprecated; it will be removed in version 2.0.
351
- While initializing your connection, use `#request(:authorization, ...)` instead.
352
- See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
353
- TEXT
354
- set_authorization_header(:authorization, type, token)
355
- end
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
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
499
427
  # a string.
500
- # @param headers [Hash] unencoded HTTP header key/value pairs.
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,15 @@ 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
+ url = url.to_s.gsub(':', '%3A') if URI.parse(url.to_s).opaque
549
477
  uri = url ? base + url : base
550
478
  if params
551
479
  uri.query = params.to_query(params_encoder || options.params_encoder)
552
480
  end
553
- # rubocop:disable Style/SafeNavigation
554
481
  uri.query = nil if uri.query && uri.query.empty?
555
- # rubocop:enable Style/SafeNavigation
556
482
  uri
557
483
  end
558
484
 
@@ -584,14 +510,6 @@ module Faraday
584
510
  yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
585
511
  end
586
512
 
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
513
  def proxy_from_env(url)
596
514
  return if Faraday.ignore_env_proxy
597
515
 
@@ -618,7 +536,7 @@ module Faraday
618
536
  end
619
537
 
620
538
  def find_default_proxy
621
- uri = ENV['http_proxy']
539
+ uri = ENV.fetch('http_proxy', nil)
622
540
  return unless uri && !uri.empty?
623
541
 
624
542
  uri = "http://#{uri}" unless uri.match?(/^http/i)
@@ -636,7 +554,7 @@ module Faraday
636
554
  end
637
555
 
638
556
  def support_parallel?(adapter)
639
- adapter&.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
557
+ adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
640
558
  end
641
559
  end
642
560
  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
@@ -102,7 +108,7 @@ module Faraday
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
data/lib/faraday/error.rb CHANGED
@@ -6,7 +6,7 @@ module Faraday
6
6
  class Error < StandardError
7
7
  attr_reader :response, :wrapped_exception
8
8
 
9
- def initialize(exc, response = nil)
9
+ def initialize(exc = nil, response = nil)
10
10
  @wrapped_exception = nil unless defined?(@wrapped_exception)
11
11
  @response = nil unless defined?(@response)
12
12
  super(exc_msg_and_response!(exc, response))
@@ -52,6 +52,7 @@ module Faraday
52
52
  # :body - Optional string HTTP response body.
53
53
  # :request - Hash
54
54
  # :method - Symbol with the request HTTP method.
55
+ # :url - URI object with the url requested.
55
56
  # :url_path - String with the url path requested.
56
57
  # :params - String key/value hash of query params
57
58
  # present in the request.
@@ -140,7 +141,7 @@ module Faraday
140
141
  class SSLError < Error
141
142
  end
142
143
 
143
- # Raised by FaradayMiddleware::ResponseMiddleware
144
+ # Raised by middlewares that parse the response, like the JSON response middleware.
144
145
  class ParsingError < Error
145
146
  end
146
147
  end
@@ -1,13 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pp'
3
+ require 'pp' # rubocop:disable Lint/RedundantRequireStatement
4
+
4
5
  module Faraday
5
6
  module Logging
6
7
  # Serves as an integration point to customize logging
7
8
  class Formatter
8
9
  extend Forwardable
9
10
 
10
- DEFAULT_OPTIONS = { headers: true, bodies: false,
11
+ DEFAULT_OPTIONS = { headers: true, bodies: false, errors: false,
11
12
  log_level: :info }.freeze
12
13
 
13
14
  def initialize(logger:, options:)
@@ -36,6 +37,18 @@ module Faraday
36
37
  log_body('response', env[:body]) if env[:body] && log_body?(:response)
37
38
  end
38
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
+
39
52
  def filter(filter_word, filter_replacement)
40
53
  @filter.push([filter_word, filter_replacement])
41
54
  end
@@ -76,6 +89,10 @@ module Faraday
76
89
  end
77
90
  end
78
91
 
92
+ def log_errors?
93
+ @options[:errors]
94
+ end
95
+
79
96
  def apply_filters(output)
80
97
  @filter.each do |pattern, replacement|
81
98
  output = output.to_s.gsub(pattern, replacement)
@@ -4,7 +4,6 @@ module Faraday
4
4
  # Middleware is the basic base class of any Faraday middleware.
5
5
  class Middleware
6
6
  extend MiddlewareRegistry
7
- extend DependencyLoader
8
7
 
9
8
  attr_reader :app, :options
10
9
 
@@ -18,6 +17,9 @@ module Faraday
18
17
  app.call(env).on_complete do |environment|
19
18
  on_complete(environment) if respond_to?(:on_complete)
20
19
  end
20
+ rescue StandardError => e
21
+ on_error(e) if respond_to?(:on_error)
22
+ raise
21
23
  end
22
24
 
23
25
  def close