faraday 2.7.0 → 2.13.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/LICENSE.md +1 -1
  4. data/README.md +29 -17
  5. data/Rakefile +6 -1
  6. data/lib/faraday/adapter/test.rb +20 -7
  7. data/lib/faraday/adapter.rb +2 -3
  8. data/lib/faraday/connection.rb +35 -32
  9. data/lib/faraday/encoders/nested_params_encoder.rb +1 -1
  10. data/lib/faraday/error.rb +59 -7
  11. data/lib/faraday/logging/formatter.rb +18 -20
  12. data/lib/faraday/middleware.rb +40 -1
  13. data/lib/faraday/options/connection_options.rb +7 -6
  14. data/lib/faraday/options/env.rb +68 -63
  15. data/lib/faraday/options/proxy_options.rb +11 -5
  16. data/lib/faraday/options/request_options.rb +7 -6
  17. data/lib/faraday/options/ssl_options.rb +57 -50
  18. data/lib/faraday/options.rb +4 -3
  19. data/lib/faraday/rack_builder.rb +21 -25
  20. data/lib/faraday/request/instrumentation.rb +3 -1
  21. data/lib/faraday/request/json.rb +18 -3
  22. data/lib/faraday/request.rb +10 -7
  23. data/lib/faraday/response/json.rb +21 -1
  24. data/lib/faraday/response/logger.rb +7 -5
  25. data/lib/faraday/response/raise_error.rb +36 -17
  26. data/lib/faraday/response.rb +2 -1
  27. data/lib/faraday/utils/headers.rb +8 -2
  28. data/lib/faraday/utils.rb +3 -4
  29. data/lib/faraday/version.rb +1 -1
  30. data/lib/faraday.rb +2 -1
  31. data/spec/faraday/adapter/test_spec.rb +29 -0
  32. data/spec/faraday/connection_spec.rb +17 -2
  33. data/spec/faraday/error_spec.rb +122 -7
  34. data/spec/faraday/middleware_spec.rb +143 -0
  35. data/spec/faraday/options/options_spec.rb +1 -1
  36. data/spec/faraday/options/proxy_options_spec.rb +35 -0
  37. data/spec/faraday/params_encoders/nested_spec.rb +2 -1
  38. data/spec/faraday/request/json_spec.rb +88 -0
  39. data/spec/faraday/response/json_spec.rb +89 -0
  40. data/spec/faraday/response/logger_spec.rb +50 -5
  41. data/spec/faraday/response/raise_error_spec.rb +112 -9
  42. data/spec/faraday/response_spec.rb +3 -1
  43. data/spec/faraday/utils/headers_spec.rb +9 -0
  44. data/spec/faraday/utils_spec.rb +3 -1
  45. data/spec/faraday_spec.rb +10 -4
  46. data/spec/spec_helper.rb +6 -5
  47. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  48. metadata +26 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aded1d146b09e5b537b81833177fcd70acbed297598160e1114807fe047c15c2
4
- data.tar.gz: 3b4c82d7712d3667767da96b87050d0cdb76bc0d0d2ed3f67d7f7ccb595f474e
3
+ metadata.gz: 12dedb476a26ae00e5f902e4e10f5327a790290d2d7647711aac09e8661af959
4
+ data.tar.gz: f43c7bb1127b0a57bb7aa0727a096e0b66cfeead93445631dacf151c354efbdb
5
5
  SHA512:
6
- metadata.gz: 6e37eda4374f7c99ea8e687a6c704fcc74935800c4209c5b94767737e2133dc343dcfaa49deda11ddd312fe6f8e8b661a765134b402d3aa787672ffc84026547
7
- data.tar.gz: 8ce5708af5954af94fdb2c1aff6a4cefcf7ed512d2f91d05c7ac3cdfc72fcad2840e9674c5defb8aaed570e5c9e5f2324911fb22e4b05184b2597a87959f2853
6
+ metadata.gz: b3cb808ea984cc306ad71fc502bf79541d5e79fb9ca066784e0674dc71a2dc22ac20b6968065cceaa3fe5ccb909124aeef9513a37bf83bbcb4a63e116dbd6afe
7
+ data.tar.gz: 1540d3fe594b1d0a025caede980b99419de50d4ea148b53bf5cf7a3082307982c0917fd739621805252f8016e427ffdbfba59da5a679b529380adf0100cf10ef
data/CHANGELOG.md CHANGED
@@ -517,7 +517,7 @@ Breaking changes:
517
517
  - Drop support for Ruby 1.8
518
518
 
519
519
  Features:
520
- - Include wrapped exception/reponse in ClientErrors
520
+ - Include wrapped exception/response in ClientErrors
521
521
  - Add `response.reason_phrase`
522
522
  - Provide option to selectively skip logging request/response headers
523
523
  - Add regex support for pattern matching in `test` adapter
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2022 Rick Olson, Zack Hobson
1
+ Copyright (c) 2009-2023 Rick Olson, Zack Hobson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,26 +1,41 @@
1
- # [![Faraday](./docs/assets/img/repo-card-slim.png)][website]
1
+ # [![Faraday](./docs/_media/home-logo.svg)][website]
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/faraday.svg)](https://rubygems.org/gems/faraday)
4
4
  [![GitHub Actions CI](https://github.com/lostisland/faraday/workflows/CI/badge.svg)](https://github.com/lostisland/faraday/actions?query=workflow%3ACI)
5
5
  [![GitHub Discussions](https://img.shields.io/github/discussions/lostisland/faraday?logo=github)](https://github.com/lostisland/faraday/discussions)
6
6
 
7
-
8
7
  Faraday is an HTTP client library abstraction layer that provides a common interface over many
9
8
  adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.
10
- You probably don't want to use Faraday directly in your project, as it will lack an actual client library to perform
11
- requests. Instead, you probably want to have a look at [Awesome Faraday][awesome] for a list of available adapters.
9
+ Take a look at [Awesome Faraday][awesome] for a list of available adapters and middleware.
10
+
11
+ ## Why use Faraday?
12
+
13
+ Faraday gives you the power of Rack middleware for manipulating HTTP requests and responses,
14
+ making it easier to build sophisticated API clients or web service libraries that abstract away
15
+ the details of how HTTP requests are made.
16
+
17
+ Faraday comes with a lot of features out of the box, such as:
18
+ * Support for multiple adapters (Net::HTTP, Typhoeus, Patron, Excon, HTTPClient, and more)
19
+ * Persistent connections (keep-alive)
20
+ * Parallel requests
21
+ * Automatic response parsing (JSON, XML, YAML)
22
+ * Customization of the request/response cycle with middleware
23
+ * Support for streaming responses
24
+ * Support for uploading files
25
+ * And much more!
12
26
 
13
27
  ## Getting Started
14
28
 
15
29
  The best starting point is the [Faraday Website][website], with its introduction and explanation.
16
- Need more details? See the [Faraday API Documentation][apidoc] to see how it works internally.
30
+
31
+ Need more details? See the [Faraday API Documentation][apidoc] to see how it works internally, or take a look at [Advanced techniques for calling HTTP APIs in Ruby](https://mattbrictson.com/blog/advanced-http-techniques-in-ruby) blog post from [@mattbrictson](https://github.com/mattbrictson) 🚀
17
32
 
18
33
  ## Supported Ruby versions
19
34
 
20
35
  This library aims to support and is [tested against][actions] the currently officially supported Ruby
21
36
  implementations. This means that, even without a major release, we could add or drop support for Ruby versions,
22
37
  following their [EOL](https://endoflife.date/ruby).
23
- Currently that means we support Ruby 2.6+
38
+ Currently that means we support Ruby 3.0+
24
39
 
25
40
  If something doesn't work on one of these Ruby versions, it's a bug.
26
41
 
@@ -42,14 +57,11 @@ Open the issues page and check for the `help wanted` label!
42
57
  But before you start coding, please read our [Contributing Guide][contributing]
43
58
 
44
59
  ## Copyright
45
- © 2009 - 2022, the [Faraday Team][faraday_team]. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
46
-
47
- [awesome]: https://github.com/lostisland/awesome-faraday/#adapters
48
- [website]: https://lostisland.github.io/faraday
49
- [faraday_team]: https://lostisland.github.io/faraday/team
50
- [contributing]: https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md
51
- [apidoc]: https://www.rubydoc.info/github/lostisland/faraday
52
- [actions]: https://github.com/lostisland/faraday/actions
53
- [jruby]: http://jruby.org/
54
- [rubinius]: http://rubini.us/
55
- [license]: LICENSE.md
60
+
61
+ © 2009 - 2023, the Faraday Team. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
62
+
63
+ [awesome]: https://github.com/lostisland/awesome-faraday/#adapters
64
+ [website]: https://lostisland.github.io/faraday
65
+ [contributing]: https://github.com/lostisland/faraday/blob/main/.github/CONTRIBUTING.md
66
+ [apidoc]: https://www.rubydoc.info/github/lostisland/faraday
67
+ [actions]: https://github.com/lostisland/faraday/actions
data/Rakefile CHANGED
@@ -1,7 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rspec/core/rake_task'
4
+ require 'bundler'
4
5
 
5
- RSpec::Core::RakeTask.new(:spec)
6
+ Bundler::GemHelper.install_tasks
7
+
8
+ RSpec::Core::RakeTask.new(:spec) do |task|
9
+ task.ruby_opts = %w[-W]
10
+ end
6
11
 
7
12
  task default: :spec
@@ -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
@@ -144,7 +146,7 @@ module Faraday
144
146
  # which means that all of a path, parameters, and headers must be the same as an actual request.
145
147
  def strict_mode=(value)
146
148
  @strict_mode = value
147
- @stack.each do |_method, stubs|
149
+ @stack.each_value do |stubs|
148
150
  stubs.each do |stub|
149
151
  stub.strict_mode = value
150
152
  end
@@ -182,7 +184,7 @@ module Faraday
182
184
  end
183
185
 
184
186
  # Stub request
185
- 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
186
188
  # @param env [Faraday::Env]
187
189
  def matches?(env)
188
190
  request_host = env[:url].host
@@ -238,7 +240,7 @@ module Faraday
238
240
  end
239
241
 
240
242
  def body_match?(request_body)
241
- return true if body.to_s.size.zero?
243
+ return true if body.to_s.empty?
242
244
 
243
245
  case body
244
246
  when Proc
@@ -273,15 +275,26 @@ module Faraday
273
275
 
274
276
  unless stub
275
277
  raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
276
- "#{env[:url]} #{env[:body]}"
278
+ "#{env[:url]} #{env[:body]} #{env[:headers]}"
277
279
  end
278
280
 
279
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
+
280
291
  status, headers, body =
281
- if block_arity >= 0
282
- 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
283
296
  else
284
- stub.block.call(env, meta)
297
+ stub.block.call(*params)
285
298
  end
286
299
 
287
300
  # We need to explicitly pass `reason_phrase = nil` here to avoid keyword args conflicts.
@@ -26,7 +26,7 @@ module Faraday
26
26
  self.supports_parallel = false
27
27
 
28
28
  def initialize(_app = nil, opts = {}, &block)
29
- @app = ->(env) { env.response }
29
+ @app = lambda(&:response)
30
30
  @connection_options = opts
31
31
  @config_block = block
32
32
  end
@@ -78,8 +78,7 @@ module Faraday
78
78
  # @param type [Symbol] Describes which timeout setting to get: :read,
79
79
  # :write, or :open.
80
80
  # @param options [Hash] Hash containing Symbol keys like :timeout,
81
- # :read_timeout, :write_timeout, :open_timeout, or
82
- # :timeout
81
+ # :read_timeout, :write_timeout, or :open_timeout
83
82
  #
84
83
  # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
85
84
  # has been set.
@@ -15,7 +15,7 @@ module Faraday
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}"
18
+ USER_AGENT = "Faraday v#{VERSION}".freeze
19
19
 
20
20
  # @return [Hash] URI query unencoded key/value pairs.
21
21
  attr_reader :params
@@ -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|
@@ -261,14 +261,13 @@ module Faraday
261
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
@@ -315,15 +314,23 @@ module Faraday
315
314
  #
316
315
  # @yield a block to execute multiple requests.
317
316
  # @return [void]
318
- def in_parallel(manager = nil)
317
+ def in_parallel(manager = nil, &block)
319
318
  @parallel_manager = manager || default_parallel_manager do
320
319
  warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
321
320
  'on Faraday stack'
322
321
  warn caller[2, 10].join("\n")
323
322
  nil
324
323
  end
325
- yield
326
- @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
327
334
  ensure
328
335
  @parallel_manager = nil
329
336
  end
@@ -424,8 +431,8 @@ module Faraday
424
431
  #
425
432
  # @param method [Symbol] HTTP method.
426
433
  # @param url [String, URI, nil] String or URI to access.
427
- # @param body [String, nil] The request body that will eventually be converted to
428
- # a string.
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.
429
436
  # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
430
437
  #
431
438
  # @return [Faraday::Response]
@@ -471,10 +478,11 @@ module Faraday
471
478
  def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
472
479
  url = nil if url.respond_to?(:empty?) && url.empty?
473
480
  base = url_prefix.dup
474
- if url && base.path && base.path !~ %r{/$}
481
+ if url && !base.path.end_with?('/')
475
482
  base.path = "#{base.path}/" # ensure trailing slash
476
483
  end
477
- url = url.to_s.gsub(':', '%3A') if url && URI.parse(url.to_s).opaque
484
+ # Ensure relative url will be parsed correctly (such as `service:search` )
485
+ url = "./#{url}" if url.respond_to?(:start_with?) && !url.start_with?('http://', 'https://', '/', './', '../')
478
486
  uri = url ? base + url : base
479
487
  if params
480
488
  uri.query = params.to_query(params_encoder || options.params_encoder)
@@ -515,22 +523,17 @@ module Faraday
515
523
  return if Faraday.ignore_env_proxy
516
524
 
517
525
  uri = nil
518
- if URI.parse('').respond_to?(:find_proxy)
519
- case url
520
- when String
521
- uri = Utils.URI(url)
522
- uri = if uri.host.nil?
523
- find_default_proxy
524
- else
525
- URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
526
- end
527
- when URI
528
- uri = url.find_proxy
529
- when nil
530
- uri = find_default_proxy
531
- end
532
- else
533
- warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
526
+ case url
527
+ when String
528
+ uri = Utils.URI(url)
529
+ uri = if uri.host.nil?
530
+ find_default_proxy
531
+ else
532
+ URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
533
+ end
534
+ when URI
535
+ uri = url.find_proxy
536
+ when nil
534
537
  uri = find_default_proxy
535
538
  end
536
539
  ProxyOptions.from(uri) if uri
@@ -102,7 +102,7 @@ module Faraday
102
102
 
103
103
  protected
104
104
 
105
- SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/.freeze
105
+ SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/
106
106
 
107
107
  def decode_pair(key, value, context)
108
108
  subkeys = key.scan(SUBKEYS_REGEX)
data/lib/faraday/error.rb CHANGED
@@ -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
@@ -73,12 +79,46 @@ module Faraday
73
79
 
74
80
  # Pulls out potential parent exception and response hash.
75
81
  def exc_msg_and_response(exc, response = nil)
76
- 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?
102
+
103
+ method = request.fetch(:method, nil)
104
+ url = request.fetch(:url, nil)
105
+ build_status_error_message(status, method, url)
106
+ end
77
107
 
78
- return [nil, "the server responded with status #{exc[:status]}", exc] \
79
- if exc.respond_to?(:each_key)
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
80
112
 
81
- [nil, exc.to_s, response]
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
118
+
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'
82
122
  end
83
123
  end
84
124
 
@@ -106,6 +146,10 @@ module Faraday
106
146
  class ProxyAuthError < ClientError
107
147
  end
108
148
 
149
+ # Raised by Faraday::Response::RaiseError in case of a 408 response.
150
+ class RequestTimeoutError < ClientError
151
+ end
152
+
109
153
  # Raised by Faraday::Response::RaiseError in case of a 409 response.
110
154
  class ConflictError < ClientError
111
155
  end
@@ -114,6 +158,10 @@ module Faraday
114
158
  class UnprocessableEntityError < ClientError
115
159
  end
116
160
 
161
+ # Raised by Faraday::Response::RaiseError in case of a 429 response.
162
+ class TooManyRequestsError < ClientError
163
+ end
164
+
117
165
  # Faraday server error class. Represents 5xx status responses.
118
166
  class ServerError < Error
119
167
  end
@@ -144,4 +192,8 @@ module Faraday
144
192
  # Raised by middlewares that parse the response, like the JSON response middleware.
145
193
  class ParsingError < Error
146
194
  end
195
+
196
+ # Raised by Faraday::Middleware and subclasses when invalid default_options are used
197
+ class InitializationError < Error
198
+ end
147
199
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'pp' # This require is necessary for Hash#pretty_inspect to work, do not remove it, people rely on it.
4
+
3
5
  module Faraday
4
6
  module Logging
5
7
  # Serves as an integration point to customize logging
@@ -11,40 +13,40 @@ module Faraday
11
13
 
12
14
  def initialize(logger:, options:)
13
15
  @logger = logger
14
- @filter = []
15
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 = []
16
21
  end
17
22
 
18
23
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
19
24
 
20
25
  def request(env)
21
- request_log = proc do
22
- "#{env.method.upcase} #{apply_filters(env.url.to_s)}"
26
+ public_send(log_level) do
27
+ "request: #{env.method.upcase} #{apply_filters(env.url.to_s)}"
23
28
  end
24
- public_send(log_level, 'request', &request_log)
25
29
 
26
30
  log_headers('request', env.request_headers) if log_headers?(:request)
27
31
  log_body('request', env[:body]) if env[:body] && log_body?(:request)
28
32
  end
29
33
 
30
34
  def response(env)
31
- status = proc { "Status #{env.status}" }
32
- public_send(log_level, 'response', &status)
35
+ public_send(log_level) { "response: Status #{env.status}" }
33
36
 
34
37
  log_headers('response', env.response_headers) if log_headers?(:response)
35
38
  log_body('response', env[:body]) if env[:body] && log_body?(:response)
36
39
  end
37
40
 
38
- def error(error)
41
+ def exception(exc)
39
42
  return unless log_errors?
40
43
 
41
- error_log = proc { error.full_message }
42
- public_send(log_level, 'error', &error_log)
44
+ public_send(log_level) { "error: #{exc.full_message}" }
43
45
 
44
- log_headers('error', error.response_headers) if error.respond_to?(:response_headers) && log_headers?(:error)
45
- return unless error.respond_to?(:response_body) && error.response_body && log_body?(:error)
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)
46
48
 
47
- log_body('error', error.response_body)
49
+ log_body('error', exc.response_body)
48
50
  end
49
51
 
50
52
  def filter(filter_word, filter_replacement)
@@ -54,6 +56,8 @@ module Faraday
54
56
  private
55
57
 
56
58
  def dump_headers(headers)
59
+ return if headers.nil?
60
+
57
61
  headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
58
62
  end
59
63
 
@@ -99,21 +103,15 @@ module Faraday
99
103
  end
100
104
 
101
105
  def log_level
102
- unless %i[debug info warn error fatal].include?(@options[:log_level])
103
- return :info
104
- end
105
-
106
106
  @options[:log_level]
107
107
  end
108
108
 
109
109
  def log_headers(type, headers)
110
- headers_log = proc { apply_filters(dump_headers(headers)) }
111
- public_send(log_level, type, &headers_log)
110
+ public_send(log_level) { "#{type}: #{apply_filters(dump_headers(headers))}" }
112
111
  end
113
112
 
114
113
  def log_body(type, body)
115
- body_log = proc { apply_filters(dump_body(body)) }
116
- public_send(log_level, type, &body_log)
114
+ public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" }
117
115
  end
118
116
  end
119
117
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'monitor'
4
+
3
5
  module Faraday
4
6
  # Middleware is the basic base class of any Faraday middleware.
5
7
  class Middleware
@@ -7,9 +9,46 @@ module Faraday
7
9
 
8
10
  attr_reader :app, :options
9
11
 
12
+ DEFAULT_OPTIONS = {}.freeze
13
+ LOCK = Mutex.new
14
+
10
15
  def initialize(app = nil, options = {})
11
16
  @app = app
12
- @options = options
17
+ @options = self.class.default_options.merge(options)
18
+ end
19
+
20
+ class << self
21
+ # Faraday::Middleware::default_options= allows user to set default options at the Faraday::Middleware
22
+ # class level.
23
+ #
24
+ # @example Set the Faraday::Response::RaiseError option, `include_request` to `false`
25
+ # my_app/config/initializers/my_faraday_middleware.rb
26
+ #
27
+ # Faraday::Response::RaiseError.default_options = { include_request: false }
28
+ #
29
+ def default_options=(options = {})
30
+ validate_default_options(options)
31
+ LOCK.synchronize do
32
+ @default_options = default_options.merge(options)
33
+ end
34
+ end
35
+
36
+ # default_options attr_reader that initializes class instance variable
37
+ # with the values of any Faraday::Middleware defaults, and merges with
38
+ # subclass defaults
39
+ def default_options
40
+ @default_options ||= DEFAULT_OPTIONS.merge(self::DEFAULT_OPTIONS)
41
+ end
42
+
43
+ private
44
+
45
+ def validate_default_options(options)
46
+ invalid_keys = options.keys.reject { |opt| self::DEFAULT_OPTIONS.key?(opt) }
47
+ return unless invalid_keys.any?
48
+
49
+ raise(Faraday::InitializationError,
50
+ "Invalid options provided. Keys not found in #{self}::DEFAULT_OPTIONS: #{invalid_keys.join(', ')}")
51
+ end
13
52
  end
14
53
 
15
54
  def call(env)
@@ -1,12 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Faraday
4
- # ConnectionOptions contains the configurable properties for a Faraday
5
- # connection object.
6
- class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
7
- :parallel_manager, :params, :headers,
8
- :builder_class)
9
-
4
+ # @!parse
5
+ # # ConnectionOptions contains the configurable properties for a Faraday
6
+ # # connection object.
7
+ # class ConnectionOptions < Options; end
8
+ ConnectionOptions = Options.new(:request, :proxy, :ssl, :builder, :url,
9
+ :parallel_manager, :params, :headers,
10
+ :builder_class) do
10
11
  options request: RequestOptions, ssl: SSLOptions
11
12
 
12
13
  memoized(:request) { self.class.options_for(:request).new }