faraday 2.8.1 → 2.14.0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/README.md +1 -1
  4. data/Rakefile +3 -0
  5. data/lib/faraday/adapter.rb +1 -1
  6. data/lib/faraday/connection.rb +16 -7
  7. data/lib/faraday/encoders/flat_params_encoder.rb +2 -2
  8. data/lib/faraday/encoders/nested_params_encoder.rb +1 -1
  9. data/lib/faraday/error.rb +46 -5
  10. data/lib/faraday/logging/formatter.rb +7 -7
  11. data/lib/faraday/middleware.rb +40 -1
  12. data/lib/faraday/options/env.rb +2 -2
  13. data/lib/faraday/options/proxy_options.rb +4 -2
  14. data/lib/faraday/options/ssl_options.rb +8 -2
  15. data/lib/faraday/rack_builder.rb +21 -25
  16. data/lib/faraday/request/json.rb +1 -1
  17. data/lib/faraday/response/json.rb +2 -1
  18. data/lib/faraday/response/logger.rb +5 -3
  19. data/lib/faraday/response/raise_error.rb +19 -19
  20. data/lib/faraday/response.rb +7 -3
  21. data/lib/faraday/utils/headers.rb +8 -2
  22. data/lib/faraday/utils.rb +3 -4
  23. data/lib/faraday/version.rb +1 -1
  24. data/lib/faraday.rb +2 -1
  25. data/spec/faraday/connection_spec.rb +2 -2
  26. data/spec/faraday/error_spec.rb +93 -3
  27. data/spec/faraday/middleware_spec.rb +143 -0
  28. data/spec/faraday/options/proxy_options_spec.rb +27 -0
  29. data/spec/faraday/params_encoders/nested_spec.rb +2 -1
  30. data/spec/faraday/response/json_spec.rb +17 -0
  31. data/spec/faraday/response/logger_spec.rb +45 -4
  32. data/spec/faraday/response/raise_error_spec.rb +97 -22
  33. data/spec/faraday/response_spec.rb +7 -0
  34. data/spec/faraday/utils/headers_spec.rb +9 -0
  35. data/spec/faraday/utils_spec.rb +3 -1
  36. data/spec/faraday_spec.rb +3 -1
  37. data/spec/spec_helper.rb +6 -5
  38. data/spec/support/faraday_middleware_subclasses.rb +18 -0
  39. metadata +22 -24
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14056963c7f6d474cd9c3a407ea2bfbe9e9fd2246b3b3a69aa57585668bc0739
4
- data.tar.gz: d8d3125754e43fdfdfbaf134d53d2e6524944cd68422f7b10b13fb587a9ff288
3
+ metadata.gz: 2fc5e44f27171827ea964dee68999e7d9ae6cd4d952a0a8ad0600e155cdf3892
4
+ data.tar.gz: 88c85684e7d9acb7978b36d20f2041145441f99e7705e6cf8f41483ed620dadc
5
5
  SHA512:
6
- metadata.gz: 026fcfad3c5696d387f4bd68c6ddbe36ef6b13a640771209954adf4fb191d27e8fcc13af61b23645cba44cf4f92c71e720a3e7d022081402143b8639ca0a8fa7
7
- data.tar.gz: 21bce5de8543dc53735214b7a35d6dcf3387f93be56e1a1c575522ce07e66d6de555fbb5673aede73f3fa9ca3fa3613e06a5aa7e9a63035b8028553f2cb4ccfe
6
+ metadata.gz: 972d764f70f42d46e01ef8c8a33aab692619b8adc1793f07ed031cc7cd09ad6d749d5948d25f4e9e8c6b83377bfa2a25eda1e80eb79d086bae93d90959e321bd
7
+ data.tar.gz: b9b7b80f59d02b813ae99ba5afd3cbaf517953f208f2cfc91347bdbec1f4112d4b508c22721f65ac607b45a47507d7adc97cbe7cf08b3d0c56ad0c7dc1b0af5e
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/README.md CHANGED
@@ -35,7 +35,7 @@ Need more details? See the [Faraday API Documentation][apidoc] to see how it wor
35
35
  This library aims to support and is [tested against][actions] the currently officially supported Ruby
36
36
  implementations. This means that, even without a major release, we could add or drop support for Ruby versions,
37
37
  following their [EOL](https://endoflife.date/ruby).
38
- Currently that means we support Ruby 2.6+
38
+ Currently that means we support Ruby 3.0+
39
39
 
40
40
  If something doesn't work on one of these Ruby versions, it's a bug.
41
41
 
data/Rakefile CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rspec/core/rake_task'
4
+ require 'bundler'
5
+
6
+ Bundler::GemHelper.install_tasks
4
7
 
5
8
  RSpec::Core::RakeTask.new(:spec) do |task|
6
9
  task.ruby_opts = %w[-W]
@@ -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
@@ -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
@@ -314,15 +314,23 @@ module Faraday
314
314
  #
315
315
  # @yield a block to execute multiple requests.
316
316
  # @return [void]
317
- def in_parallel(manager = nil)
317
+ def in_parallel(manager = nil, &block)
318
318
  @parallel_manager = manager || default_parallel_manager do
319
319
  warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
320
320
  'on Faraday stack'
321
321
  warn caller[2, 10].join("\n")
322
322
  nil
323
323
  end
324
- yield
325
- @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
326
334
  ensure
327
335
  @parallel_manager = nil
328
336
  end
@@ -423,8 +431,8 @@ module Faraday
423
431
  #
424
432
  # @param method [Symbol] HTTP method.
425
433
  # @param url [String, URI, nil] String or URI to access.
426
- # @param body [String, nil] The request body that will eventually be converted to
427
- # 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.
428
436
  # @param headers [Hash, nil] unencoded HTTP header key/value pairs.
429
437
  #
430
438
  # @return [Faraday::Response]
@@ -473,7 +481,8 @@ module Faraday
473
481
  if url && !base.path.end_with?('/')
474
482
  base.path = "#{base.path}/" # ensure trailing slash
475
483
  end
476
- url = url.to_s.gsub(':', '%3A') if 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://', '/', './', '../')
477
486
  uri = url ? base + url : base
478
487
  if params
479
488
  uri.query = params.to_query(params_encoder || options.params_encoder)
@@ -76,9 +76,9 @@ module Faraday
76
76
 
77
77
  empty_accumulator = {}
78
78
 
79
- split_query = (query.split('&').map do |pair|
79
+ split_query = query.split('&').filter_map do |pair|
80
80
  pair.split('=', 2) if pair && !pair.empty?
81
- end).compact
81
+ end
82
82
  split_query.each_with_object(empty_accumulator.dup) do |pair, accu|
83
83
  pair[0] = unescape(pair[0])
84
84
  pair[1] = true if pair[1].nil?
@@ -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
@@ -79,12 +79,46 @@ module Faraday
79
79
 
80
80
  # Pulls out potential parent exception and response hash.
81
81
  def exc_msg_and_response(exc, response = nil)
82
- 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
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
83
112
 
84
- return [nil, "the server responded with status #{exc[:status]}", exc] \
85
- if exc.respond_to?(:each_key)
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
86
118
 
87
- [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'
88
122
  end
89
123
  end
90
124
 
@@ -121,9 +155,12 @@ module Faraday
121
155
  end
122
156
 
123
157
  # Raised by Faraday::Response::RaiseError in case of a 422 response.
124
- class UnprocessableEntityError < ClientError
158
+ class UnprocessableContentError < ClientError
125
159
  end
126
160
 
161
+ # Used to provide compatibility with legacy error name.
162
+ UnprocessableEntityError = UnprocessableContentError
163
+
127
164
  # Raised by Faraday::Response::RaiseError in case of a 429 response.
128
165
  class TooManyRequestsError < ClientError
129
166
  end
@@ -158,4 +195,8 @@ module Faraday
158
195
  # Raised by middlewares that parse the response, like the JSON response middleware.
159
196
  class ParsingError < Error
160
197
  end
198
+
199
+ # Raised by Faraday::Middleware and subclasses when invalid default_options are used
200
+ class InitializationError < Error
201
+ end
161
202
  end
@@ -23,8 +23,8 @@ module Faraday
23
23
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
24
24
 
25
25
  def request(env)
26
- public_send(log_level, 'request') do
27
- "#{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)}"
28
28
  end
29
29
 
30
30
  log_headers('request', env.request_headers) if log_headers?(:request)
@@ -32,7 +32,7 @@ module Faraday
32
32
  end
33
33
 
34
34
  def response(env)
35
- public_send(log_level, 'response') { "Status #{env.status}" }
35
+ public_send(log_level) { "response: Status #{env.status}" }
36
36
 
37
37
  log_headers('response', env.response_headers) if log_headers?(:response)
38
38
  log_body('response', env[:body]) if env[:body] && log_body?(:response)
@@ -41,7 +41,7 @@ module Faraday
41
41
  def exception(exc)
42
42
  return unless log_errors?
43
43
 
44
- public_send(log_level, 'error') { exc.full_message }
44
+ public_send(log_level) { "error: #{exc.full_message}" }
45
45
 
46
46
  log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error)
47
47
  return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error)
@@ -63,7 +63,7 @@ module Faraday
63
63
 
64
64
  def dump_body(body)
65
65
  if body.respond_to?(:to_str)
66
- body.to_str
66
+ body.to_str.encode(Encoding::UTF_8, undef: :replace, invalid: :replace)
67
67
  else
68
68
  pretty_inspect(body)
69
69
  end
@@ -107,11 +107,11 @@ module Faraday
107
107
  end
108
108
 
109
109
  def log_headers(type, headers)
110
- public_send(log_level, type) { apply_filters(dump_headers(headers)) }
110
+ public_send(log_level) { "#{type}: #{apply_filters(dump_headers(headers))}" }
111
111
  end
112
112
 
113
113
  def log_body(type, body)
114
- public_send(log_level, type) { apply_filters(dump_body(body)) }
114
+ public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" }
115
115
  end
116
116
  end
117
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)
@@ -60,7 +60,7 @@ module Faraday
60
60
  :reason_phrase, :response_body) do
61
61
  const_set(:ContentLength, 'Content-Length')
62
62
  const_set(:StatusesWithoutBody, Set.new([204, 304]))
63
- const_set(:SuccessfulStatuses, (200..299).freeze)
63
+ const_set(:SuccessfulStatuses, 200..299)
64
64
 
65
65
  # A Set of HTTP verbs that typically send a body. If no body is set for
66
66
  # these requests, the Content-Length header is set to 0.
@@ -169,7 +169,7 @@ module Faraday
169
169
  def stream_response(&block)
170
170
  size = 0
171
171
  yielded = false
172
- block_result = block.call do |chunk| # rubocop:disable Performance/RedundantBlockCall
172
+ block_result = block.call do |chunk|
173
173
  if chunk.bytesize.positive? || size.positive?
174
174
  yielded = true
175
175
  size += chunk.bytesize
@@ -22,8 +22,10 @@ module Faraday
22
22
  when URI
23
23
  value = { uri: value }
24
24
  when Hash, Options
25
- if (uri = value.delete(:uri))
26
- value[:uri] = Utils.URI(uri)
25
+ if value[:uri]
26
+ value = value.dup.tap do |duped|
27
+ duped[:uri] = Utils.URI(duped[:uri])
28
+ end
27
29
  end
28
30
  end
29
31
 
@@ -11,6 +11,9 @@ module Faraday
11
11
  # # @return [Boolean] whether to enable hostname verification on server certificates
12
12
  # # during the handshake or not (see https://github.com/ruby/openssl/pull/60)
13
13
  # #
14
+ # # @!attribute hostname
15
+ # # @return [String] Server hostname used for SNI (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D)
16
+ # #
14
17
  # # @!attribute ca_file
15
18
  # # @return [String] CA file
16
19
  # #
@@ -46,12 +49,15 @@ module Faraday
46
49
  # #
47
50
  # # @!attribute max_version
48
51
  # # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
52
+ # #
53
+ # # @!attribute ciphers
54
+ # # @return [String] cipher list in OpenSSL format (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)
49
55
  # class SSLOptions < Options; end
50
- SSLOptions = Options.new(:verify, :verify_hostname,
56
+ SSLOptions = Options.new(:verify, :verify_hostname, :hostname,
51
57
  :ca_file, :ca_path, :verify_mode,
52
58
  :cert_store, :client_cert, :client_key,
53
59
  :certificate, :private_key, :verify_depth,
54
- :version, :min_version, :max_version) do
60
+ :version, :min_version, :max_version, :ciphers) do
55
61
  # @return [Boolean] true if should verify
56
62
  def verify?
57
63
  verify != false
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'ruby2_keywords'
4
3
  require 'faraday/adapter_registry'
5
4
 
6
5
  module Faraday
@@ -28,10 +27,11 @@ module Faraday
28
27
 
29
28
  attr_reader :name
30
29
 
31
- ruby2_keywords def initialize(klass, *args, &block)
30
+ def initialize(klass, *args, **kwargs, &block)
32
31
  @name = klass.to_s
33
32
  REGISTRY.set(klass) if klass.respond_to?(:name)
34
33
  @args = args
34
+ @kwargs = kwargs
35
35
  @block = block
36
36
  end
37
37
 
@@ -54,7 +54,7 @@ module Faraday
54
54
  end
55
55
 
56
56
  def build(app = nil)
57
- klass.new(app, *@args, &@block)
57
+ klass.new(app, *@args, **@kwargs, &@block)
58
58
  end
59
59
  end
60
60
 
@@ -89,52 +89,52 @@ module Faraday
89
89
  @handlers.frozen?
90
90
  end
91
91
 
92
- ruby2_keywords def use(klass, *args, &block)
92
+ def use(klass, ...)
93
93
  if klass.is_a? Symbol
94
- use_symbol(Faraday::Middleware, klass, *args, &block)
94
+ use_symbol(Faraday::Middleware, klass, ...)
95
95
  else
96
96
  raise_if_locked
97
97
  raise_if_adapter(klass)
98
- @handlers << self.class::Handler.new(klass, *args, &block)
98
+ @handlers << self.class::Handler.new(klass, ...)
99
99
  end
100
100
  end
101
101
 
102
- ruby2_keywords def request(key, *args, &block)
103
- use_symbol(Faraday::Request, key, *args, &block)
102
+ def request(key, ...)
103
+ use_symbol(Faraday::Request, key, ...)
104
104
  end
105
105
 
106
- ruby2_keywords def response(key, *args, &block)
107
- use_symbol(Faraday::Response, key, *args, &block)
106
+ def response(...)
107
+ use_symbol(Faraday::Response, ...)
108
108
  end
109
109
 
110
- ruby2_keywords def adapter(klass = NO_ARGUMENT, *args, &block)
110
+ def adapter(klass = NO_ARGUMENT, *args, **kwargs, &block)
111
111
  return @adapter if klass == NO_ARGUMENT || klass.nil?
112
112
 
113
113
  klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
114
- @adapter = self.class::Handler.new(klass, *args, &block)
114
+ @adapter = self.class::Handler.new(klass, *args, **kwargs, &block)
115
115
  end
116
116
 
117
117
  ## methods to push onto the various positions in the stack:
118
118
 
119
- ruby2_keywords def insert(index, *args, &block)
119
+ def insert(index, ...)
120
120
  raise_if_locked
121
121
  index = assert_index(index)
122
- handler = self.class::Handler.new(*args, &block)
122
+ handler = self.class::Handler.new(...)
123
123
  @handlers.insert(index, handler)
124
124
  end
125
125
 
126
126
  alias insert_before insert
127
127
 
128
- ruby2_keywords def insert_after(index, *args, &block)
128
+ def insert_after(index, ...)
129
129
  index = assert_index(index)
130
- insert(index + 1, *args, &block)
130
+ insert(index + 1, ...)
131
131
  end
132
132
 
133
- ruby2_keywords def swap(index, *args, &block)
133
+ def swap(index, ...)
134
134
  raise_if_locked
135
135
  index = assert_index(index)
136
136
  @handlers.delete_at(index)
137
- insert(index, *args, &block)
137
+ insert(index, ...)
138
138
  end
139
139
 
140
140
  def delete(handler)
@@ -221,7 +221,7 @@ module Faraday
221
221
  end
222
222
 
223
223
  def raise_if_adapter(klass)
224
- return unless is_adapter?(klass)
224
+ return unless klass <= Faraday::Adapter
225
225
 
226
226
  raise 'Adapter should be set using the `adapter` method, not `use`'
227
227
  end
@@ -234,12 +234,8 @@ module Faraday
234
234
  !@adapter.nil?
235
235
  end
236
236
 
237
- def is_adapter?(klass) # rubocop:disable Naming/PredicateName
238
- klass <= Faraday::Adapter
239
- end
240
-
241
- ruby2_keywords def use_symbol(mod, key, *args, &block)
242
- use(mod.lookup_middleware(key), *args, &block)
237
+ def use_symbol(mod, key, ...)
238
+ use(mod.lookup_middleware(key), ...)
243
239
  end
244
240
 
245
241
  def assert_index(index)
@@ -13,7 +13,7 @@ module Faraday
13
13
  # Doesn't try to encode bodies that already are in string form.
14
14
  class Json < Middleware
15
15
  MIME_TYPE = 'application/json'
16
- MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze
16
+ MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
17
17
 
18
18
  def on_request(env)
19
19
  match_content_type(env) do |data|
@@ -60,7 +60,8 @@ module Faraday
60
60
  @decoder_options =
61
61
  if @decoder_options.is_a?(Array) && @decoder_options.size >= 2
62
62
  @decoder_options.slice(0, 2)
63
- elsif @decoder_options.respond_to?(:load)
63
+ elsif @decoder_options&.respond_to?(:load) # rubocop:disable Lint/RedundantSafeNavigation
64
+ # In some versions of Rails, `nil` responds to `load` - hence the safe navigation check above
64
65
  [@decoder_options, :load]
65
66
  else
66
67
  [::JSON, :parse]
@@ -10,11 +10,13 @@ module Faraday
10
10
  # lifecycle to a given Logger object. By default, this logs to STDOUT. See
11
11
  # Faraday::Logging::Formatter to see specifically what is logged.
12
12
  class Logger < Middleware
13
+ DEFAULT_OPTIONS = { formatter: Logging::Formatter }.merge(Logging::Formatter::DEFAULT_OPTIONS).freeze
14
+
13
15
  def initialize(app, logger = nil, options = {})
14
- super(app)
16
+ super(app, options)
15
17
  logger ||= ::Logger.new($stdout)
16
- formatter_class = options.delete(:formatter) || Logging::Formatter
17
- @formatter = formatter_class.new(logger: logger, options: options)
18
+ formatter_class = @options.delete(:formatter)
19
+ @formatter = formatter_class.new(logger: logger, options: @options)
18
20
  yield @formatter if block_given?
19
21
  end
20
22
 
@@ -6,32 +6,32 @@ module Faraday
6
6
  # client or server error responses.
7
7
  class RaiseError < Middleware
8
8
  # rubocop:disable Naming/ConstantName
9
- ClientErrorStatuses = (400...500).freeze
10
- ServerErrorStatuses = (500...600).freeze
9
+ ClientErrorStatuses = (400...500)
10
+ ServerErrorStatuses = (500...600)
11
+ ClientErrorStatusesWithCustomExceptions = {
12
+ 400 => Faraday::BadRequestError,
13
+ 401 => Faraday::UnauthorizedError,
14
+ 403 => Faraday::ForbiddenError,
15
+ 404 => Faraday::ResourceNotFound,
16
+ 408 => Faraday::RequestTimeoutError,
17
+ 409 => Faraday::ConflictError,
18
+ 422 => Faraday::UnprocessableContentError,
19
+ 429 => Faraday::TooManyRequestsError
20
+ }.freeze
11
21
  # rubocop:enable Naming/ConstantName
12
22
 
23
+ DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze
24
+
13
25
  def on_complete(env)
26
+ return if Array(options[:allowed_statuses]).include?(env[:status])
27
+
14
28
  case env[:status]
15
- when 400
16
- raise Faraday::BadRequestError, response_values(env)
17
- when 401
18
- raise Faraday::UnauthorizedError, response_values(env)
19
- when 403
20
- raise Faraday::ForbiddenError, response_values(env)
21
- when 404
22
- raise Faraday::ResourceNotFound, response_values(env)
29
+ when *ClientErrorStatusesWithCustomExceptions.keys
30
+ raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env)
23
31
  when 407
24
32
  # mimic the behavior that we get with proxy requests with HTTPS
25
33
  msg = %(407 "Proxy Authentication Required")
26
34
  raise Faraday::ProxyAuthError.new(msg, response_values(env))
27
- when 408
28
- raise Faraday::RequestTimeoutError, response_values(env)
29
- when 409
30
- raise Faraday::ConflictError, response_values(env)
31
- when 422
32
- raise Faraday::UnprocessableEntityError, response_values(env)
33
- when 429
34
- raise Faraday::TooManyRequestsError, response_values(env)
35
35
  when ClientErrorStatuses
36
36
  raise Faraday::ClientError, response_values(env)
37
37
  when ServerErrorStatuses
@@ -58,7 +58,7 @@ module Faraday
58
58
 
59
59
  # Include the request data by default. If the middleware was explicitly
60
60
  # configured to _not_ include request data, then omit it.
61
- return response unless options.fetch(:include_request, true)
61
+ return response unless options[:include_request]
62
62
 
63
63
  response.merge(
64
64
  request: {
@@ -33,6 +33,10 @@ module Faraday
33
33
  finished? ? env.body : nil
34
34
  end
35
35
 
36
+ def url
37
+ finished? ? env.url : nil
38
+ end
39
+
36
40
  def finished?
37
41
  !!env
38
42
  end
@@ -60,9 +64,9 @@ module Faraday
60
64
 
61
65
  def to_hash
62
66
  {
63
- status: env.status, body: env.body,
64
- response_headers: env.response_headers,
65
- url: env.url
67
+ status: status, body: body,
68
+ response_headers: headers,
69
+ url: url
66
70
  }
67
71
  end
68
72
 
@@ -62,10 +62,10 @@ module Faraday
62
62
  super(key, val)
63
63
  end
64
64
 
65
- def fetch(key, *args, &block)
65
+ def fetch(key, ...)
66
66
  key = KeyMap[key]
67
67
  key = @names.fetch(key.downcase, key)
68
- super(key, *args, &block)
68
+ super(key, ...)
69
69
  end
70
70
 
71
71
  def delete(key)
@@ -77,6 +77,12 @@ module Faraday
77
77
  super(key)
78
78
  end
79
79
 
80
+ def dig(key, *rest)
81
+ key = KeyMap[key]
82
+ key = @names.fetch(key.downcase, key)
83
+ super(key, *rest)
84
+ end
85
+
80
86
  def include?(key)
81
87
  @names.include? key.downcase
82
88
  end