faraday 1.4.1 → 2.14.2

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 (94) 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 +67 -13
  7. data/examples/client_test.rb +81 -16
  8. data/lib/faraday/adapter/test.rb +117 -52
  9. data/lib/faraday/adapter.rb +12 -20
  10. data/lib/faraday/connection.rb +86 -134
  11. data/lib/faraday/encoders/flat_params_encoder.rb +3 -2
  12. data/lib/faraday/encoders/nested_params_encoder.rb +15 -7
  13. data/lib/faraday/error.rb +65 -15
  14. data/lib/faraday/logging/formatter.rb +30 -17
  15. data/lib/faraday/middleware.rb +44 -3
  16. data/lib/faraday/middleware_registry.rb +17 -63
  17. data/lib/faraday/options/connection_options.rb +7 -6
  18. data/lib/faraday/options/env.rb +88 -65
  19. data/lib/faraday/options/proxy_options.rb +17 -6
  20. data/lib/faraday/options/request_options.rb +8 -7
  21. data/lib/faraday/options/ssl_options.rb +62 -45
  22. data/lib/faraday/options.rb +8 -7
  23. data/lib/faraday/rack_builder.rb +46 -47
  24. data/lib/faraday/request/authorization.rb +37 -38
  25. data/lib/faraday/request/instrumentation.rb +5 -1
  26. data/lib/faraday/request/json.rb +70 -0
  27. data/lib/faraday/request/url_encoded.rb +5 -1
  28. data/lib/faraday/request.rb +20 -37
  29. data/lib/faraday/response/json.rb +74 -0
  30. data/lib/faraday/response/logger.rb +13 -7
  31. data/lib/faraday/response/raise_error.rb +45 -18
  32. data/lib/faraday/response.rb +15 -21
  33. data/lib/faraday/utils/headers.rb +18 -7
  34. data/lib/faraday/utils.rb +11 -7
  35. data/lib/faraday/version.rb +1 -1
  36. data/lib/faraday.rb +12 -32
  37. data/spec/faraday/adapter/test_spec.rb +182 -0
  38. data/spec/faraday/connection_spec.rb +219 -92
  39. data/spec/faraday/error_spec.rb +122 -7
  40. data/spec/faraday/middleware_registry_spec.rb +31 -0
  41. data/spec/faraday/middleware_spec.rb +163 -2
  42. data/spec/faraday/options/env_spec.rb +8 -2
  43. data/spec/faraday/options/options_spec.rb +1 -1
  44. data/spec/faraday/options/proxy_options_spec.rb +42 -0
  45. data/spec/faraday/params_encoders/nested_spec.rb +10 -1
  46. data/spec/faraday/rack_builder_spec.rb +26 -54
  47. data/spec/faraday/request/authorization_spec.rb +54 -24
  48. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  49. data/spec/faraday/request/json_spec.rb +199 -0
  50. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  51. data/spec/faraday/request_spec.rb +14 -15
  52. data/spec/faraday/response/json_spec.rb +206 -0
  53. data/spec/faraday/response/logger_spec.rb +84 -5
  54. data/spec/faraday/response/raise_error_spec.rb +133 -16
  55. data/spec/faraday/response_spec.rb +10 -1
  56. data/spec/faraday/utils/headers_spec.rb +31 -4
  57. data/spec/faraday/utils_spec.rb +66 -2
  58. data/spec/faraday_spec.rb +10 -4
  59. data/spec/spec_helper.rb +6 -5
  60. data/spec/support/fake_safe_buffer.rb +1 -1
  61. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  62. data/spec/support/helper_methods.rb +0 -37
  63. data/spec/support/shared_examples/adapter.rb +2 -2
  64. data/spec/support/shared_examples/request_method.rb +22 -21
  65. metadata +27 -81
  66. data/lib/faraday/adapter/em_http.rb +0 -289
  67. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
  68. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
  69. data/lib/faraday/adapter/em_synchrony.rb +0 -153
  70. data/lib/faraday/adapter/httpclient.rb +0 -152
  71. data/lib/faraday/adapter/patron.rb +0 -132
  72. data/lib/faraday/adapter/rack.rb +0 -75
  73. data/lib/faraday/adapter/typhoeus.rb +0 -15
  74. data/lib/faraday/autoload.rb +0 -92
  75. data/lib/faraday/dependency_loader.rb +0 -37
  76. data/lib/faraday/file_part.rb +0 -128
  77. data/lib/faraday/param_part.rb +0 -53
  78. data/lib/faraday/request/basic_authentication.rb +0 -20
  79. data/lib/faraday/request/multipart.rb +0 -106
  80. data/lib/faraday/request/retry.rb +0 -239
  81. data/lib/faraday/request/token_authentication.rb +0 -20
  82. data/spec/faraday/adapter/em_http_spec.rb +0 -47
  83. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -16
  84. data/spec/faraday/adapter/excon_spec.rb +0 -49
  85. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  86. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  87. data/spec/faraday/adapter/patron_spec.rb +0 -18
  88. data/spec/faraday/adapter/rack_spec.rb +0 -8
  89. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  90. data/spec/faraday/composite_read_io_spec.rb +0 -80
  91. data/spec/faraday/request/multipart_spec.rb +0 -302
  92. data/spec/faraday/request/retry_spec.rb +0 -242
  93. data/spec/faraday/response/middleware_spec.rb +0 -68
  94. data/spec/support/webmock_rack_app.rb +0 -68
@@ -6,15 +6,16 @@ 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
  #
15
15
  class Connection
16
16
  # A Set of allowed HTTP verbs.
17
17
  METHODS = Set.new %i[get post put delete head patch options trace]
18
+ USER_AGENT = "Faraday v#{VERSION}".freeze
18
19
 
19
20
  # @return [Hash] URI query unencoded key/value pairs.
20
21
  attr_reader :params
@@ -26,7 +27,7 @@ module Faraday
26
27
  # Connection. This includes a default host name, scheme, port, and path.
27
28
  attr_reader :url_prefix
28
29
 
29
- # @return [Faraday::Builder] Builder for this Connection.
30
+ # @return [Faraday::RackBuilder] Builder for this Connection.
30
31
  attr_reader :builder
31
32
 
32
33
  # @return [Hash] SSL options.
@@ -63,7 +64,7 @@ module Faraday
63
64
  options = ConnectionOptions.from(options)
64
65
 
65
66
  if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
66
- options = options.merge(url)
67
+ options = Utils.deep_merge(options, url)
67
68
  url = options.url
68
69
  end
69
70
 
@@ -73,6 +74,7 @@ module Faraday
73
74
  @options = options.request
74
75
  @ssl = options.ssl
75
76
  @default_parallel_manager = options.parallel_manager
77
+ @manual_proxy = nil
76
78
 
77
79
  @builder = options.builder || begin
78
80
  # pass an empty block to Builder so it doesn't assume default middleware
@@ -88,7 +90,7 @@ module Faraday
88
90
 
89
91
  yield(self) if block_given?
90
92
 
91
- @headers[:user_agent] ||= "Faraday v#{VERSION}"
93
+ @headers[:user_agent] ||= USER_AGENT
92
94
  end
93
95
 
94
96
  def initialize_proxy(url, options)
@@ -115,7 +117,7 @@ module Faraday
115
117
 
116
118
  extend Forwardable
117
119
 
118
- def_delegators :builder, :build, :use, :request, :response, :adapter, :app
120
+ def_delegators :builder, :use, :request, :response, :adapter, :app
119
121
 
120
122
  # Closes the underlying resources and/or connections. In the case of
121
123
  # persistent connections, this closes all currently open connections
@@ -128,10 +130,10 @@ module Faraday
128
130
  # Makes a GET HTTP request without a body.
129
131
  # @!scope class
130
132
  #
131
- # @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
132
134
  # all requests. Can also be the options Hash.
133
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
134
- # @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.
135
137
  #
136
138
  # @example
137
139
  # conn.get '/items', { page: 1 }, :accept => 'application/json'
@@ -150,10 +152,10 @@ module Faraday
150
152
  # Makes a HEAD HTTP request without a body.
151
153
  # @!scope class
152
154
  #
153
- # @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
154
156
  # all requests. Can also be the options Hash.
155
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
156
- # @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.
157
159
  #
158
160
  # @example
159
161
  # conn.head '/items/1'
@@ -165,10 +167,10 @@ module Faraday
165
167
  # Makes a DELETE HTTP request without a body.
166
168
  # @!scope class
167
169
  #
168
- # @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
169
171
  # all requests. Can also be the options Hash.
170
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
171
- # @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.
172
174
  #
173
175
  # @example
174
176
  # conn.delete '/items/1'
@@ -180,10 +182,10 @@ module Faraday
180
182
  # Makes a TRACE HTTP request without a body.
181
183
  # @!scope class
182
184
  #
183
- # @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
184
186
  # all requests. Can also be the options Hash.
185
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
186
- # @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.
187
189
  #
188
190
  # @example
189
191
  # conn.connect '/items/1'
@@ -208,9 +210,9 @@ module Faraday
208
210
  #
209
211
  # @overload options(url, params = nil, headers = nil)
210
212
  # Makes an OPTIONS HTTP request to the given URL.
211
- # @param url [String] String base URL to sue as a prefix for all requests.
212
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
213
- # @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.
214
216
  #
215
217
  # @example
216
218
  # conn.options '/items/1'
@@ -218,7 +220,7 @@ module Faraday
218
220
  # @yield [Faraday::Request] for further request customizations
219
221
  # @return [Faraday::Response]
220
222
  def options(*args)
221
- return @options if args.size.zero?
223
+ return @options if args.empty?
222
224
 
223
225
  url, params, headers = *args
224
226
  run_request(:options, url, nil, headers) do |request|
@@ -231,10 +233,10 @@ module Faraday
231
233
  # Makes a POST HTTP request with a body.
232
234
  # @!scope class
233
235
  #
234
- # @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
235
237
  # all requests. Can also be the options Hash.
236
- # @param body [String] body for the request.
237
- # @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.
238
240
  #
239
241
  # @example
240
242
  # conn.post '/items', data, content_type: 'application/json'
@@ -253,20 +255,19 @@ module Faraday
253
255
  # Makes a PUT HTTP request with a body.
254
256
  # @!scope class
255
257
  #
256
- # @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
257
259
  # all requests. Can also be the options Hash.
258
- # @param body [String] body for the request.
259
- # @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.
260
262
  #
261
263
  # @example
262
- # # TODO: Make it a PUT example
263
- # conn.post '/items', data, content_type: 'application/json'
264
+ # conn.put '/products/123', data, content_type: 'application/json'
264
265
  #
265
- # # Simple ElasticSearch indexing sample.
266
- # conn.post '/twitter/tweet' do |req|
267
- # req.headers[:content_type] = 'application/json'
268
- # req.params[:routing] = 'kimchy'
269
- # 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'
270
271
  # end
271
272
  #
272
273
  # @yield [Faraday::Request] for further request customizations
@@ -281,62 +282,6 @@ module Faraday
281
282
  RUBY
282
283
  end
283
284
 
284
- # Sets up the Authorization header with these credentials, encoded
285
- # with base64.
286
- #
287
- # @param login [String] The authentication login.
288
- # @param pass [String] The authentication password.
289
- #
290
- # @example
291
- #
292
- # conn.basic_auth 'Aladdin', 'open sesame'
293
- # conn.headers['Authorization']
294
- # # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
295
- #
296
- # @return [void]
297
- def basic_auth(login, pass)
298
- set_authorization_header(:basic_auth, login, pass)
299
- end
300
-
301
- # Sets up the Authorization header with the given token.
302
- #
303
- # @param token [String]
304
- # @param options [Hash] extra token options.
305
- #
306
- # @example
307
- #
308
- # conn.token_auth 'abcdef', foo: 'bar'
309
- # conn.headers['Authorization']
310
- # # => "Token token=\"abcdef\",
311
- # foo=\"bar\""
312
- #
313
- # @return [void]
314
- def token_auth(token, options = nil)
315
- set_authorization_header(:token_auth, token, options)
316
- end
317
-
318
- # Sets up a custom Authorization header.
319
- #
320
- # @param type [String] authorization type
321
- # @param token [String, Hash] token. A String value is taken literally, and
322
- # a Hash is encoded into comma-separated key/value pairs.
323
- #
324
- # @example
325
- #
326
- # conn.authorization :Bearer, 'mF_9.B5f-4.1JqM'
327
- # conn.headers['Authorization']
328
- # # => "Bearer mF_9.B5f-4.1JqM"
329
- #
330
- # conn.authorization :Token, token: 'abcdef', foo: 'bar'
331
- # conn.headers['Authorization']
332
- # # => "Token token=\"abcdef\",
333
- # foo=\"bar\""
334
- #
335
- # @return [void]
336
- def authorization(type, token)
337
- set_authorization_header(:authorization, type, token)
338
- end
339
-
340
285
  # Check if the adapter is parallel-capable.
341
286
  #
342
287
  # @yield if the adapter isn't parallel-capable, or if no adapter is set yet.
@@ -369,15 +314,23 @@ module Faraday
369
314
  #
370
315
  # @yield a block to execute multiple requests.
371
316
  # @return [void]
372
- def in_parallel(manager = nil)
317
+ def in_parallel(manager = nil, &block)
373
318
  @parallel_manager = manager || default_parallel_manager do
374
319
  warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
375
320
  'on Faraday stack'
376
321
  warn caller[2, 10].join("\n")
377
322
  nil
378
323
  end
379
- yield
380
- @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
381
334
  ensure
382
335
  @parallel_manager = nil
383
336
  end
@@ -403,11 +356,11 @@ module Faraday
403
356
  # @example
404
357
  #
405
358
  # conn = Faraday::Connection.new { ... }
406
- # conn.url_prefix = "https://sushi.com/api"
359
+ # conn.url_prefix = "https://httpbingo.org/api"
407
360
  # conn.scheme # => https
408
361
  # conn.path_prefix # => "/api"
409
362
  #
410
- # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
363
+ # conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri
411
364
  def url_prefix=(url, encoder = nil)
412
365
  uri = @url_prefix = Utils.URI(url)
413
366
  self.path_prefix = uri.path
@@ -416,9 +369,16 @@ module Faraday
416
369
  uri.query = nil
417
370
 
418
371
  with_uri_credentials(uri) do |user, password|
419
- basic_auth user, password
372
+ set_basic_auth(user, password)
420
373
  uri.user = uri.password = nil
421
374
  end
375
+
376
+ @proxy = proxy_from_env(url) unless @manual_proxy
377
+ end
378
+
379
+ def set_basic_auth(user, password)
380
+ header = Faraday::Utils.basic_header_from(user, password)
381
+ headers[Faraday::Request::Authorization::KEY] = header
422
382
  end
423
383
 
424
384
  # Sets the path prefix and ensures that it always has a leading
@@ -437,20 +397,20 @@ module Faraday
437
397
  # Takes a relative url for a request and combines it with the defaults
438
398
  # set on the connection instance.
439
399
  #
440
- # @param url [String]
400
+ # @param url [String, URI, nil]
441
401
  # @param extra_params [Hash]
442
402
  #
443
403
  # @example
444
404
  # conn = Faraday::Connection.new { ... }
445
- # conn.url_prefix = "https://sushi.com/api?token=abc"
405
+ # conn.url_prefix = "https://httpbingo.org/api?token=abc"
446
406
  # conn.scheme # => https
447
407
  # conn.path_prefix # => "/api"
448
408
  #
449
409
  # conn.build_url("nigiri?page=2")
450
- # # => https://sushi.com/api/nigiri?token=abc&page=2
410
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
451
411
  #
452
412
  # conn.build_url("nigiri", page: 2)
453
- # # => https://sushi.com/api/nigiri?token=abc&page=2
413
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
454
414
  #
455
415
  def build_url(url = nil, extra_params = nil)
456
416
  uri = build_exclusive_url(url)
@@ -470,10 +430,10 @@ module Faraday
470
430
  # Builds and runs the Faraday::Request.
471
431
  #
472
432
  # @param method [Symbol] HTTP method.
473
- # @param url [String, URI] String or URI to access.
474
- # @param body [Object] The request body that will eventually be converted to
475
- # a string.
476
- # @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.
477
437
  #
478
438
  # @return [Faraday::Response]
479
439
  def run_request(method, url, body, headers)
@@ -509,7 +469,7 @@ module Faraday
509
469
 
510
470
  # Build an absolute URL based on url_prefix.
511
471
  #
512
- # @param url [String, URI]
472
+ # @param url [String, URI, nil]
513
473
  # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
514
474
  # replace the query values
515
475
  # of the resulting url (default: nil).
@@ -517,19 +477,20 @@ module Faraday
517
477
  # @return [URI]
518
478
  def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
519
479
  url = nil if url.respond_to?(:empty?) && url.empty?
520
- base = url_prefix
521
- if url && base.path && base.path !~ %r{/$}
522
- base = base.dup
480
+ base = url_prefix.dup
481
+ if url && !base.path.end_with?('/')
523
482
  base.path = "#{base.path}/" # ensure trailing slash
524
483
  end
525
- url = url && URI.parse(url.to_s).opaque ? url.to_s.gsub(':', '%3A') : url
484
+ url = url.to_s if url.respond_to?(:host)
485
+ # Ensure relative url will be parsed correctly (such as `service:search` or `//evil.com`)
486
+ url = "./#{url}" if url.respond_to?(:start_with?) &&
487
+ (url.start_with?('//') ||
488
+ !url.start_with?('http://', 'https://', '/', './', '../'))
526
489
  uri = url ? base + url : base
527
490
  if params
528
491
  uri.query = params.to_query(params_encoder || options.params_encoder)
529
492
  end
530
- # rubocop:disable Style/SafeNavigation
531
493
  uri.query = nil if uri.query && uri.query.empty?
532
- # rubocop:enable Style/SafeNavigation
533
494
  uri
534
495
  end
535
496
 
@@ -561,37 +522,28 @@ module Faraday
561
522
  yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
562
523
  end
563
524
 
564
- def set_authorization_header(header_type, *args)
565
- header = Faraday::Request
566
- .lookup_middleware(header_type)
567
- .header(*args)
568
-
569
- headers[Faraday::Request::Authorization::KEY] = header
570
- end
571
-
572
525
  def proxy_from_env(url)
573
526
  return if Faraday.ignore_env_proxy
574
527
 
575
528
  uri = nil
576
- if URI.parse('').respond_to?(:find_proxy)
577
- case url
578
- when String
579
- uri = Utils.URI(url)
580
- uri = URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
581
- when URI
582
- uri = url.find_proxy
583
- when nil
584
- uri = find_default_proxy
585
- end
586
- else
587
- warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
529
+ case url
530
+ when String
531
+ uri = Utils.URI(url)
532
+ uri = if uri.host.nil?
533
+ find_default_proxy
534
+ else
535
+ URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
536
+ end
537
+ when URI
538
+ uri = url.find_proxy
539
+ when nil
588
540
  uri = find_default_proxy
589
541
  end
590
542
  ProxyOptions.from(uri) if uri
591
543
  end
592
544
 
593
545
  def find_default_proxy
594
- uri = ENV['http_proxy']
546
+ uri = ENV.fetch('http_proxy', nil)
595
547
  return unless uri && !uri.empty?
596
548
 
597
549
  uri = "http://#{uri}" unless uri.match?(/^http/i)
@@ -609,7 +561,7 @@ module Faraday
609
561
  end
610
562
 
611
563
  def support_parallel?(adapter)
612
- adapter&.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
564
+ adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
613
565
  end
614
566
  end
615
567
  end
@@ -6,6 +6,7 @@ module Faraday
6
6
  module FlatParamsEncoder
7
7
  class << self
8
8
  extend Forwardable
9
+
9
10
  def_delegators :'Faraday::Utils', :escape, :unescape
10
11
  end
11
12
 
@@ -76,9 +77,9 @@ module Faraday
76
77
 
77
78
  empty_accumulator = {}
78
79
 
79
- split_query = (query.split('&').map do |pair|
80
+ split_query = query.split('&').filter_map do |pair|
80
81
  pair.split('=', 2) if pair && !pair.empty?
81
- end).compact
82
+ end
82
83
  split_query.each_with_object(empty_accumulator.dup) do |pair, accu|
83
84
  pair[0] = unescape(pair[0])
84
85
  pair[1] = true if pair[1].nil?
@@ -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,14 +167,16 @@ 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
173
+
167
174
  def_delegators :'Faraday::Utils', :escape, :unescape
168
175
  end
169
176
 
170
177
  # Useful default for OAuth and caching.
171
178
  @sort_params = true
179
+ @array_indices = false
172
180
 
173
181
  extend EncodeMethods
174
182
  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))
@@ -29,15 +29,21 @@ module Faraday
29
29
  end
30
30
 
31
31
  def response_status
32
- @response[:status] if @response
32
+ return unless @response
33
+
34
+ @response.is_a?(Faraday::Response) ? @response.status : @response[:status]
33
35
  end
34
36
 
35
37
  def response_headers
36
- @response[:headers] if @response
38
+ return unless @response
39
+
40
+ @response.is_a?(Faraday::Response) ? @response.headers : @response[:headers]
37
41
  end
38
42
 
39
43
  def response_body
40
- @response[:body] if @response
44
+ return unless @response
45
+
46
+ @response.is_a?(Faraday::Response) ? @response.body : @response[:body]
41
47
  end
42
48
 
43
49
  protected
@@ -52,6 +58,7 @@ module Faraday
52
58
  # :body - Optional string HTTP response body.
53
59
  # :request - Hash
54
60
  # :method - Symbol with the request HTTP method.
61
+ # :url - URI object with the url requested.
55
62
  # :url_path - String with the url path requested.
56
63
  # :params - String key/value hash of query params
57
64
  # present in the request.
@@ -72,12 +79,46 @@ module Faraday
72
79
 
73
80
  # Pulls out potential parent exception and response hash.
74
81
  def exc_msg_and_response(exc, response = nil)
75
- return [exc, exc.message, response] if exc.respond_to?(:backtrace)
82
+ case exc
83
+ when Exception
84
+ [exc, exc.message, response]
85
+ when Hash
86
+ [nil, build_error_message_from_hash(exc), exc]
87
+ when Faraday::Env
88
+ [nil, build_error_message_from_env(exc), exc]
89
+ else
90
+ [nil, exc.to_s, response]
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def build_error_message_from_hash(hash)
97
+ # Be defensive with external Hash objects - they might be missing keys
98
+ status = hash.fetch(:status, nil)
99
+ request = hash.fetch(:request, nil)
100
+
101
+ return fallback_error_message(status) if request.nil?
76
102
 
77
- return [nil, "the server responded with status #{exc[:status]}", exc] \
78
- if exc.respond_to?(:each_key)
103
+ method = request.fetch(:method, nil)
104
+ url = request.fetch(:url, nil)
105
+ build_status_error_message(status, method, url)
106
+ end
107
+
108
+ def build_error_message_from_env(env)
109
+ # Faraday::Env is internal - we can make reasonable assumptions about its structure
110
+ build_status_error_message(env.status, env.method, env.url)
111
+ end
112
+
113
+ def build_status_error_message(status, method, url)
114
+ method_str = method ? method.to_s.upcase : ''
115
+ url_str = url ? url.to_s : ''
116
+ "the server responded with status #{status} for #{method_str} #{url_str}"
117
+ end
79
118
 
80
- [nil, exc.to_s, response]
119
+ def fallback_error_message(status)
120
+ "the server responded with status #{status} - method and url are not available " \
121
+ 'due to include_request: false on Faraday::Response::RaiseError middleware'
81
122
  end
82
123
  end
83
124
 
@@ -105,12 +146,23 @@ module Faraday
105
146
  class ProxyAuthError < ClientError
106
147
  end
107
148
 
149
+ # Raised by Faraday::Response::RaiseError in case of a 408 response.
150
+ class RequestTimeoutError < ClientError
151
+ end
152
+
108
153
  # Raised by Faraday::Response::RaiseError in case of a 409 response.
109
154
  class ConflictError < ClientError
110
155
  end
111
156
 
112
157
  # Raised by Faraday::Response::RaiseError in case of a 422 response.
113
- class UnprocessableEntityError < ClientError
158
+ class UnprocessableContentError < ClientError
159
+ end
160
+
161
+ # Used to provide compatibility with legacy error name.
162
+ UnprocessableEntityError = UnprocessableContentError
163
+
164
+ # Raised by Faraday::Response::RaiseError in case of a 429 response.
165
+ class TooManyRequestsError < ClientError
114
166
  end
115
167
 
116
168
  # Faraday server error class. Represents 5xx status responses.
@@ -120,7 +172,7 @@ module Faraday
120
172
  # A unified client error for timeouts.
121
173
  class TimeoutError < ServerError
122
174
  def initialize(exc = 'timeout', response = nil)
123
- super(exc, response)
175
+ super
124
176
  end
125
177
  end
126
178
 
@@ -140,13 +192,11 @@ module Faraday
140
192
  class SSLError < Error
141
193
  end
142
194
 
143
- # Raised by FaradayMiddleware::ResponseMiddleware
195
+ # Raised by middlewares that parse the response, like the JSON response middleware.
144
196
  class ParsingError < Error
145
197
  end
146
198
 
147
- # Exception used to control the Retry middleware.
148
- #
149
- # @see Faraday::Request::Retry
150
- class RetriableResponse < Error
199
+ # Raised by Faraday::Middleware and subclasses when invalid default_options are used
200
+ class InitializationError < Error
151
201
  end
152
202
  end