faraday 1.1.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -1
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -21
  5. data/Rakefile +3 -1
  6. data/examples/client_spec.rb +67 -13
  7. data/examples/client_test.rb +80 -15
  8. data/lib/faraday/adapter/test.rb +117 -52
  9. data/lib/faraday/adapter.rb +5 -20
  10. data/lib/faraday/connection.rb +70 -129
  11. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  12. data/lib/faraday/error.rb +29 -8
  13. data/lib/faraday/logging/formatter.rb +28 -15
  14. data/lib/faraday/methods.rb +6 -0
  15. data/lib/faraday/middleware.rb +17 -5
  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 +85 -62
  19. data/lib/faraday/options/proxy_options.rb +11 -3
  20. data/lib/faraday/options/request_options.rb +7 -6
  21. data/lib/faraday/options/ssl_options.rb +56 -45
  22. data/lib/faraday/options.rb +7 -6
  23. data/lib/faraday/rack_builder.rb +23 -21
  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 +73 -0
  30. data/lib/faraday/response/logger.rb +8 -4
  31. data/lib/faraday/response/raise_error.rb +33 -6
  32. data/lib/faraday/response.rb +10 -26
  33. data/lib/faraday/utils/headers.rb +7 -2
  34. data/lib/faraday/utils.rb +11 -7
  35. data/lib/faraday/version.rb +5 -0
  36. data/lib/faraday.rb +49 -58
  37. data/spec/faraday/adapter/test_spec.rb +182 -0
  38. data/spec/faraday/connection_spec.rb +207 -90
  39. data/spec/faraday/error_spec.rb +45 -5
  40. data/spec/faraday/middleware_registry_spec.rb +31 -0
  41. data/spec/faraday/middleware_spec.rb +50 -6
  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 +15 -0
  45. data/spec/faraday/params_encoders/nested_spec.rb +8 -0
  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 +5 -15
  52. data/spec/faraday/response/json_spec.rb +189 -0
  53. data/spec/faraday/response/logger_spec.rb +38 -0
  54. data/spec/faraday/response/raise_error_spec.rb +77 -5
  55. data/spec/faraday/response_spec.rb +3 -1
  56. data/spec/faraday/utils/headers_spec.rb +22 -4
  57. data/spec/faraday/utils_spec.rb +63 -1
  58. data/spec/faraday_spec.rb +8 -4
  59. data/spec/spec_helper.rb +6 -5
  60. data/spec/support/fake_safe_buffer.rb +1 -1
  61. data/spec/support/helper_methods.rb +0 -37
  62. data/spec/support/shared_examples/adapter.rb +4 -3
  63. data/spec/support/shared_examples/request_method.rb +58 -29
  64. metadata +17 -57
  65. data/lib/faraday/adapter/em_http.rb +0 -286
  66. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
  67. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
  68. data/lib/faraday/adapter/em_synchrony.rb +0 -150
  69. data/lib/faraday/adapter/excon.rb +0 -124
  70. data/lib/faraday/adapter/httpclient.rb +0 -152
  71. data/lib/faraday/adapter/net_http.rb +0 -219
  72. data/lib/faraday/adapter/net_http_persistent.rb +0 -91
  73. data/lib/faraday/adapter/patron.rb +0 -132
  74. data/lib/faraday/adapter/rack.rb +0 -75
  75. data/lib/faraday/adapter/typhoeus.rb +0 -15
  76. data/lib/faraday/autoload.rb +0 -95
  77. data/lib/faraday/dependency_loader.rb +0 -39
  78. data/lib/faraday/file_part.rb +0 -128
  79. data/lib/faraday/param_part.rb +0 -53
  80. data/lib/faraday/request/basic_authentication.rb +0 -20
  81. data/lib/faraday/request/multipart.rb +0 -106
  82. data/lib/faraday/request/retry.rb +0 -239
  83. data/lib/faraday/request/token_authentication.rb +0 -20
  84. data/spec/faraday/adapter/em_http_spec.rb +0 -47
  85. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -16
  86. data/spec/faraday/adapter/excon_spec.rb +0 -49
  87. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  88. data/spec/faraday/adapter/net_http_persistent_spec.rb +0 -57
  89. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  90. data/spec/faraday/adapter/patron_spec.rb +0 -18
  91. data/spec/faraday/adapter/rack_spec.rb +0 -8
  92. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  93. data/spec/faraday/composite_read_io_spec.rb +0 -80
  94. data/spec/faraday/request/multipart_spec.rb +0 -302
  95. data/spec/faraday/request/retry_spec.rb +0 -242
  96. data/spec/faraday/response/middleware_spec.rb +0 -68
  97. 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.
@@ -403,11 +348,11 @@ module Faraday
403
348
  # @example
404
349
  #
405
350
  # conn = Faraday::Connection.new { ... }
406
- # conn.url_prefix = "https://sushi.com/api"
351
+ # conn.url_prefix = "https://httpbingo.org/api"
407
352
  # conn.scheme # => https
408
353
  # conn.path_prefix # => "/api"
409
354
  #
410
- # conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
355
+ # conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri
411
356
  def url_prefix=(url, encoder = nil)
412
357
  uri = @url_prefix = Utils.URI(url)
413
358
  self.path_prefix = uri.path
@@ -416,9 +361,16 @@ module Faraday
416
361
  uri.query = nil
417
362
 
418
363
  with_uri_credentials(uri) do |user, password|
419
- basic_auth user, password
364
+ set_basic_auth(user, password)
420
365
  uri.user = uri.password = nil
421
366
  end
367
+
368
+ @proxy = proxy_from_env(url) unless @manual_proxy
369
+ end
370
+
371
+ def set_basic_auth(user, password)
372
+ header = Faraday::Utils.basic_header_from(user, password)
373
+ headers[Faraday::Request::Authorization::KEY] = header
422
374
  end
423
375
 
424
376
  # Sets the path prefix and ensures that it always has a leading
@@ -437,20 +389,20 @@ module Faraday
437
389
  # Takes a relative url for a request and combines it with the defaults
438
390
  # set on the connection instance.
439
391
  #
440
- # @param url [String]
392
+ # @param url [String, URI, nil]
441
393
  # @param extra_params [Hash]
442
394
  #
443
395
  # @example
444
396
  # conn = Faraday::Connection.new { ... }
445
- # conn.url_prefix = "https://sushi.com/api?token=abc"
397
+ # conn.url_prefix = "https://httpbingo.org/api?token=abc"
446
398
  # conn.scheme # => https
447
399
  # conn.path_prefix # => "/api"
448
400
  #
449
401
  # conn.build_url("nigiri?page=2")
450
- # # => https://sushi.com/api/nigiri?token=abc&page=2
402
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
451
403
  #
452
404
  # conn.build_url("nigiri", page: 2)
453
- # # => https://sushi.com/api/nigiri?token=abc&page=2
405
+ # # => https://httpbingo.org/api/nigiri?token=abc&page=2
454
406
  #
455
407
  def build_url(url = nil, extra_params = nil)
456
408
  uri = build_exclusive_url(url)
@@ -470,10 +422,10 @@ module Faraday
470
422
  # Builds and runs the Faraday::Request.
471
423
  #
472
424
  # @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
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
475
427
  # a string.
476
- # @param headers [Hash] unencoded HTTP header key/value pairs.
428
+ # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
477
429
  #
478
430
  # @return [Faraday::Response]
479
431
  def run_request(method, url, body, headers)
@@ -509,7 +461,7 @@ module Faraday
509
461
 
510
462
  # Build an absolute URL based on url_prefix.
511
463
  #
512
- # @param url [String, URI]
464
+ # @param url [String, URI, nil]
513
465
  # @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
514
466
  # replace the query values
515
467
  # of the resulting url (default: nil).
@@ -517,18 +469,16 @@ module Faraday
517
469
  # @return [URI]
518
470
  def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
519
471
  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
472
+ base = url_prefix.dup
473
+ if url && !base.path.end_with?('/')
523
474
  base.path = "#{base.path}/" # ensure trailing slash
524
475
  end
476
+ url = url.to_s.gsub(':', '%3A') if URI.parse(url.to_s).opaque
525
477
  uri = url ? base + url : base
526
478
  if params
527
479
  uri.query = params.to_query(params_encoder || options.params_encoder)
528
480
  end
529
- # rubocop:disable Style/SafeNavigation
530
481
  uri.query = nil if uri.query && uri.query.empty?
531
- # rubocop:enable Style/SafeNavigation
532
482
  uri
533
483
  end
534
484
 
@@ -560,37 +510,28 @@ module Faraday
560
510
  yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
561
511
  end
562
512
 
563
- def set_authorization_header(header_type, *args)
564
- header = Faraday::Request
565
- .lookup_middleware(header_type)
566
- .header(*args)
567
-
568
- headers[Faraday::Request::Authorization::KEY] = header
569
- end
570
-
571
513
  def proxy_from_env(url)
572
514
  return if Faraday.ignore_env_proxy
573
515
 
574
516
  uri = nil
575
- if URI.parse('').respond_to?(:find_proxy)
576
- case url
577
- when String
578
- uri = Utils.URI(url)
579
- uri = URI.parse("#{uri.scheme}://#{uri.hostname}").find_proxy
580
- when URI
581
- uri = url.find_proxy
582
- when nil
583
- uri = find_default_proxy
584
- end
585
- else
586
- warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
517
+ case url
518
+ when String
519
+ uri = Utils.URI(url)
520
+ uri = if uri.host.nil?
521
+ find_default_proxy
522
+ else
523
+ URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
524
+ end
525
+ when URI
526
+ uri = url.find_proxy
527
+ when nil
587
528
  uri = find_default_proxy
588
529
  end
589
530
  ProxyOptions.from(uri) if uri
590
531
  end
591
532
 
592
533
  def find_default_proxy
593
- uri = ENV['http_proxy']
534
+ uri = ENV.fetch('http_proxy', nil)
594
535
  return unless uri && !uri.empty?
595
536
 
596
537
  uri = "http://#{uri}" unless uri.match?(/^http/i)
@@ -608,7 +549,7 @@ module Faraday
608
549
  end
609
550
 
610
551
  def support_parallel?(adapter)
611
- adapter&.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
552
+ adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
612
553
  end
613
554
  end
614
555
  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
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))
@@ -28,6 +28,24 @@ module Faraday
28
28
  %(#<#{self.class}#{inner}>)
29
29
  end
30
30
 
31
+ def response_status
32
+ return unless @response
33
+
34
+ @response.is_a?(Faraday::Response) ? @response.status : @response[:status]
35
+ end
36
+
37
+ def response_headers
38
+ return unless @response
39
+
40
+ @response.is_a?(Faraday::Response) ? @response.headers : @response[:headers]
41
+ end
42
+
43
+ def response_body
44
+ return unless @response
45
+
46
+ @response.is_a?(Faraday::Response) ? @response.body : @response[:body]
47
+ end
48
+
31
49
  protected
32
50
 
33
51
  # Pulls out potential parent exception and response hash, storing them in
@@ -40,6 +58,7 @@ module Faraday
40
58
  # :body - Optional string HTTP response body.
41
59
  # :request - Hash
42
60
  # :method - Symbol with the request HTTP method.
61
+ # :url - URI object with the url requested.
43
62
  # :url_path - String with the url path requested.
44
63
  # :params - String key/value hash of query params
45
64
  # present in the request.
@@ -93,6 +112,10 @@ module Faraday
93
112
  class ProxyAuthError < ClientError
94
113
  end
95
114
 
115
+ # Raised by Faraday::Response::RaiseError in case of a 408 response.
116
+ class RequestTimeoutError < ClientError
117
+ end
118
+
96
119
  # Raised by Faraday::Response::RaiseError in case of a 409 response.
97
120
  class ConflictError < ClientError
98
121
  end
@@ -101,6 +124,10 @@ module Faraday
101
124
  class UnprocessableEntityError < ClientError
102
125
  end
103
126
 
127
+ # Raised by Faraday::Response::RaiseError in case of a 429 response.
128
+ class TooManyRequestsError < ClientError
129
+ end
130
+
104
131
  # Faraday server error class. Represents 5xx status responses.
105
132
  class ServerError < Error
106
133
  end
@@ -128,13 +155,7 @@ module Faraday
128
155
  class SSLError < Error
129
156
  end
130
157
 
131
- # Raised by FaradayMiddleware::ResponseMiddleware
158
+ # Raised by middlewares that parse the response, like the JSON response middleware.
132
159
  class ParsingError < Error
133
160
  end
134
-
135
- # Exception used to control the Retry middleware.
136
- #
137
- # @see Faraday::Request::Retry
138
- class RetriableResponse < Error
139
- end
140
161
  end
@@ -1,41 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pp'
3
+ require 'pp' # This require is necessary for Hash#pretty_inspect to work, do not remove it, people rely on it.
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:)
14
15
  @logger = logger
15
- @filter = []
16
16
  @options = DEFAULT_OPTIONS.merge(options)
17
+ unless %i[debug info warn error fatal].include?(@options[:log_level])
18
+ @options[:log_level] = :info
19
+ end
20
+ @filter = []
17
21
  end
18
22
 
19
23
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
20
24
 
21
25
  def request(env)
22
- request_log = proc do
26
+ public_send(log_level, 'request') do
23
27
  "#{env.method.upcase} #{apply_filters(env.url.to_s)}"
24
28
  end
25
- public_send(log_level, 'request', &request_log)
26
29
 
27
30
  log_headers('request', env.request_headers) if log_headers?(:request)
28
31
  log_body('request', env[:body]) if env[:body] && log_body?(:request)
29
32
  end
30
33
 
31
34
  def response(env)
32
- status = proc { "Status #{env.status}" }
33
- public_send(log_level, 'response', &status)
35
+ public_send(log_level, 'response') { "Status #{env.status}" }
34
36
 
35
37
  log_headers('response', env.response_headers) if log_headers?(:response)
36
38
  log_body('response', env[:body]) if env[:body] && log_body?(:response)
37
39
  end
38
40
 
41
+ def exception(exc)
42
+ return unless log_errors?
43
+
44
+ public_send(log_level, 'error') { exc.full_message }
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
@@ -43,6 +56,8 @@ module Faraday
43
56
  private
44
57
 
45
58
  def dump_headers(headers)
59
+ return if headers.nil?
60
+
46
61
  headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
47
62
  end
48
63
 
@@ -76,6 +91,10 @@ module Faraday
76
91
  end
77
92
  end
78
93
 
94
+ def log_errors?
95
+ @options[:errors]
96
+ end
97
+
79
98
  def apply_filters(output)
80
99
  @filter.each do |pattern, replacement|
81
100
  output = output.to_s.gsub(pattern, replacement)
@@ -84,21 +103,15 @@ module Faraday
84
103
  end
85
104
 
86
105
  def log_level
87
- unless %i[debug info warn error fatal].include?(@options[:log_level])
88
- return :info
89
- end
90
-
91
106
  @options[:log_level]
92
107
  end
93
108
 
94
109
  def log_headers(type, headers)
95
- headers_log = proc { apply_filters(dump_headers(headers)) }
96
- public_send(log_level, type, &headers_log)
110
+ public_send(log_level, type) { apply_filters(dump_headers(headers)) }
97
111
  end
98
112
 
99
113
  def log_body(type, body)
100
- body_log = proc { apply_filters(dump_body(body)) }
101
- public_send(log_level, type, &body_log)
114
+ public_send(log_level, type) { apply_filters(dump_body(body)) }
102
115
  end
103
116
  end
104
117
  end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ METHODS_WITH_QUERY = %w[get head delete trace].freeze
5
+ METHODS_WITH_BODY = %w[post put patch].freeze
6
+ end
@@ -4,17 +4,29 @@ 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
- def initialize(app = nil)
8
+ attr_reader :app, :options
9
+
10
+ def initialize(app = nil, options = {})
10
11
  @app = app
12
+ @options = options
13
+ end
14
+
15
+ def call(env)
16
+ on_request(env) if respond_to?(:on_request)
17
+ app.call(env).on_complete do |environment|
18
+ on_complete(environment) if respond_to?(:on_complete)
19
+ end
20
+ rescue StandardError => e
21
+ on_error(e) if respond_to?(:on_error)
22
+ raise
11
23
  end
12
24
 
13
25
  def close
14
- if @app.respond_to?(:close)
15
- @app.close
26
+ if app.respond_to?(:close)
27
+ app.close
16
28
  else
17
- warn "#{@app} does not implement \#close!"
29
+ warn "#{app} does not implement \#close!"
18
30
  end
19
31
  end
20
32
  end