faraday 2.12.0 → 2.14.3
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.
- checksums.yaml +4 -4
- data/examples/client_test.rb +1 -1
- data/lib/faraday/adapter/test.rb +8 -0
- data/lib/faraday/adapter.rb +7 -6
- data/lib/faraday/connection.rb +5 -2
- data/lib/faraday/encoders/flat_params_encoder.rb +3 -2
- data/lib/faraday/encoders/nested_params_encoder.rb +11 -1
- data/lib/faraday/error.rb +43 -6
- data/lib/faraday/logging/formatter.rb +7 -7
- data/lib/faraday/middleware.rb +1 -1
- data/lib/faraday/options/env.rb +4 -4
- data/lib/faraday/options/proxy_options.rb +6 -3
- data/lib/faraday/options/request_options.rb +1 -1
- data/lib/faraday/options/ssl_options.rb +4 -1
- data/lib/faraday/options.rb +1 -1
- data/lib/faraday/rack_builder.rb +26 -29
- data/lib/faraday/response/logger.rb +5 -3
- data/lib/faraday/response/raise_error.rb +1 -1
- data/lib/faraday/response.rb +7 -3
- data/lib/faraday/utils/headers.rb +5 -5
- data/lib/faraday/version.rb +1 -1
- data/lib/faraday.rb +2 -1
- data/spec/faraday/adapter/test_spec.rb +18 -0
- data/spec/faraday/connection_spec.rb +52 -0
- data/spec/faraday/error_spec.rb +93 -3
- data/spec/faraday/middleware_spec.rb +2 -2
- data/spec/faraday/options/proxy_options_spec.rb +27 -0
- data/spec/faraday/params_encoders/nested_spec.rb +28 -0
- data/spec/faraday/request_spec.rb +9 -0
- data/spec/faraday/response/json_spec.rb +1 -1
- data/spec/faraday/response/logger_spec.rb +46 -5
- data/spec/faraday/response/raise_error_spec.rb +24 -13
- data/spec/faraday/response_spec.rb +7 -0
- data/spec/faraday/utils_spec.rb +2 -1
- metadata +6 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ecc5e9592d7434d7e39bc5cc827be5b62af99e258582468c8b6fd78e0122bf7e
|
|
4
|
+
data.tar.gz: 4f5728ac524a3456a800c8d0159b4569173b1c841f80fae7d0d2704e261b6588
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 12567175cd7d08a95ccf8203bffcb9bebf8d1b52f279ce034c3f3e17dc5efc81e08259c2992ff792560ed626dd59ae305c163cb43018860b3c0ae833c3f68855
|
|
7
|
+
data.tar.gz: bb786c688b4629c6bc43e4715715fcd42de120eee3802a3614eca60e17627ff77f40298af500a431158b1f1de8bd0d0bd58817a8793bab92e11bac9aa9b482f2
|
data/examples/client_test.rb
CHANGED
|
@@ -26,7 +26,7 @@ class Client
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
# Example API client test
|
|
29
|
-
class ClientTest < Test::Unit::TestCase
|
|
29
|
+
class ClientTest < Test::Unit::TestCase # rubocop:disable Style/OneClassPerFile
|
|
30
30
|
def test_httpbingo_name
|
|
31
31
|
stubs = Faraday::Adapter::Test::Stubs.new
|
|
32
32
|
stubs.get('/api') do |env|
|
data/lib/faraday/adapter/test.rb
CHANGED
|
@@ -127,6 +127,14 @@ module Faraday
|
|
|
127
127
|
new_stub(:options, path, headers, &block)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
+
# Removes all stubs, including the ones that have already been consumed.
|
|
131
|
+
def clear
|
|
132
|
+
@stubs_mutex.synchronize do
|
|
133
|
+
@stack.clear
|
|
134
|
+
@consumed.clear
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
130
138
|
# Raises an error if any of the stubbed calls have not been made.
|
|
131
139
|
def verify_stubbed_calls
|
|
132
140
|
failed_stubs = []
|
data/lib/faraday/adapter.rb
CHANGED
|
@@ -8,6 +8,12 @@ module Faraday
|
|
|
8
8
|
|
|
9
9
|
CONTENT_LENGTH = 'Content-Length'
|
|
10
10
|
|
|
11
|
+
TIMEOUT_KEYS = {
|
|
12
|
+
read: :read_timeout,
|
|
13
|
+
open: :open_timeout,
|
|
14
|
+
write: :write_timeout
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
11
17
|
# This module marks an Adapter as supporting parallel requests.
|
|
12
18
|
module Parallelism
|
|
13
19
|
attr_writer :supports_parallel
|
|
@@ -23,6 +29,7 @@ module Faraday
|
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
extend Parallelism
|
|
32
|
+
|
|
26
33
|
self.supports_parallel = false
|
|
27
34
|
|
|
28
35
|
def initialize(_app = nil, opts = {}, &block)
|
|
@@ -89,12 +96,6 @@ module Faraday
|
|
|
89
96
|
end
|
|
90
97
|
options[key] || options[:timeout]
|
|
91
98
|
end
|
|
92
|
-
|
|
93
|
-
TIMEOUT_KEYS = {
|
|
94
|
-
read: :read_timeout,
|
|
95
|
-
open: :open_timeout,
|
|
96
|
-
write: :write_timeout
|
|
97
|
-
}.freeze
|
|
98
99
|
end
|
|
99
100
|
end
|
|
100
101
|
|
data/lib/faraday/connection.rb
CHANGED
|
@@ -481,8 +481,11 @@ module Faraday
|
|
|
481
481
|
if url && !base.path.end_with?('/')
|
|
482
482
|
base.path = "#{base.path}/" # ensure trailing slash
|
|
483
483
|
end
|
|
484
|
-
|
|
485
|
-
|
|
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://', '/', './', '../'))
|
|
486
489
|
uri = url ? base + url : base
|
|
487
490
|
if params
|
|
488
491
|
uri.query = params.to_query(params_encoder || options.params_encoder)
|
|
@@ -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 =
|
|
80
|
+
split_query = query.split('&').filter_map do |pair|
|
|
80
81
|
pair.split('=', 2) if pair && !pair.empty?
|
|
81
|
-
end
|
|
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?
|
|
@@ -106,6 +106,8 @@ module Faraday
|
|
|
106
106
|
|
|
107
107
|
def decode_pair(key, value, context)
|
|
108
108
|
subkeys = key.scan(SUBKEYS_REGEX)
|
|
109
|
+
validate_params_depth!(subkeys.length)
|
|
110
|
+
|
|
109
111
|
subkeys.each_with_index do |subkey, i|
|
|
110
112
|
is_array = subkey =~ /[\[\]]+\Z/
|
|
111
113
|
subkey = Regexp.last_match.pre_match if is_array
|
|
@@ -145,6 +147,12 @@ module Faraday
|
|
|
145
147
|
is_array ? context << value : context[subkey] = value
|
|
146
148
|
end
|
|
147
149
|
|
|
150
|
+
def validate_params_depth!(depth)
|
|
151
|
+
return unless @param_depth_limit && depth > @param_depth_limit
|
|
152
|
+
|
|
153
|
+
raise Faraday::Error, "exceeded nested parameter depth limit of #{@param_depth_limit}"
|
|
154
|
+
end
|
|
155
|
+
|
|
148
156
|
# Internal: convert a nested hash with purely numeric keys into an array.
|
|
149
157
|
# FIXME: this is not compatible with Rack::Utils.parse_nested_query
|
|
150
158
|
# @!visibility private
|
|
@@ -167,15 +175,17 @@ module Faraday
|
|
|
167
175
|
# for your requests.
|
|
168
176
|
module NestedParamsEncoder
|
|
169
177
|
class << self
|
|
170
|
-
attr_accessor :sort_params, :array_indices
|
|
178
|
+
attr_accessor :sort_params, :array_indices, :param_depth_limit
|
|
171
179
|
|
|
172
180
|
extend Forwardable
|
|
181
|
+
|
|
173
182
|
def_delegators :'Faraday::Utils', :escape, :unescape
|
|
174
183
|
end
|
|
175
184
|
|
|
176
185
|
# Useful default for OAuth and caching.
|
|
177
186
|
@sort_params = true
|
|
178
187
|
@array_indices = false
|
|
188
|
+
@param_depth_limit = 100
|
|
179
189
|
|
|
180
190
|
extend EncodeMethods
|
|
181
191
|
extend DecodeMethods
|
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
|
-
|
|
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?
|
|
83
102
|
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
86
112
|
|
|
87
|
-
|
|
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'
|
|
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
|
|
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
|
|
@@ -135,7 +172,7 @@ module Faraday
|
|
|
135
172
|
# A unified client error for timeouts.
|
|
136
173
|
class TimeoutError < ServerError
|
|
137
174
|
def initialize(exc = 'timeout', response = nil)
|
|
138
|
-
super
|
|
175
|
+
super
|
|
139
176
|
end
|
|
140
177
|
end
|
|
141
178
|
|
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
114
|
+
public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" }
|
|
115
115
|
end
|
|
116
116
|
end
|
|
117
117
|
end
|
data/lib/faraday/middleware.rb
CHANGED
data/lib/faraday/options/env.rb
CHANGED
|
@@ -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,
|
|
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.
|
|
@@ -78,7 +78,7 @@ module Faraday
|
|
|
78
78
|
# @param value [Object] a value fitting Option.from(v).
|
|
79
79
|
# @return [Env] from given value
|
|
80
80
|
def self.from(value)
|
|
81
|
-
env = super
|
|
81
|
+
env = super
|
|
82
82
|
if value.respond_to?(:custom_members)
|
|
83
83
|
env.custom_members.update(value.custom_members)
|
|
84
84
|
end
|
|
@@ -90,7 +90,7 @@ module Faraday
|
|
|
90
90
|
return self[current_body] if key == :body
|
|
91
91
|
|
|
92
92
|
if in_member_set?(key)
|
|
93
|
-
super
|
|
93
|
+
super
|
|
94
94
|
else
|
|
95
95
|
custom_members[key]
|
|
96
96
|
end
|
|
@@ -105,7 +105,7 @@ module Faraday
|
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
if in_member_set?(key)
|
|
108
|
-
super
|
|
108
|
+
super
|
|
109
109
|
else
|
|
110
110
|
custom_members[key] = value
|
|
111
111
|
end
|
|
@@ -7,6 +7,7 @@ module Faraday
|
|
|
7
7
|
# class ProxyOptions < Options; end
|
|
8
8
|
ProxyOptions = Options.new(:uri, :user, :password) do
|
|
9
9
|
extend Forwardable
|
|
10
|
+
|
|
10
11
|
def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=,
|
|
11
12
|
:path, :path=
|
|
12
13
|
|
|
@@ -22,12 +23,14 @@ module Faraday
|
|
|
22
23
|
when URI
|
|
23
24
|
value = { uri: value }
|
|
24
25
|
when Hash, Options
|
|
25
|
-
if
|
|
26
|
-
value
|
|
26
|
+
if value[:uri]
|
|
27
|
+
value = value.dup.tap do |duped|
|
|
28
|
+
duped[:uri] = Utils.URI(duped[:uri])
|
|
29
|
+
end
|
|
27
30
|
end
|
|
28
31
|
end
|
|
29
32
|
|
|
30
|
-
super
|
|
33
|
+
super
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
memoized(:user) { uri&.user && Utils.unescape(uri.user) }
|
|
@@ -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
|
# #
|
|
@@ -50,7 +53,7 @@ module Faraday
|
|
|
50
53
|
# # @!attribute ciphers
|
|
51
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)
|
|
52
55
|
# class SSLOptions < Options; end
|
|
53
|
-
SSLOptions = Options.new(:verify, :verify_hostname,
|
|
56
|
+
SSLOptions = Options.new(:verify, :verify_hostname, :hostname,
|
|
54
57
|
:ca_file, :ca_path, :verify_mode,
|
|
55
58
|
:cert_store, :client_cert, :client_key,
|
|
56
59
|
:certificate, :private_key, :verify_depth,
|
data/lib/faraday/options.rb
CHANGED
data/lib/faraday/rack_builder.rb
CHANGED
|
@@ -15,6 +15,11 @@ module Faraday
|
|
|
15
15
|
# Used to detect missing arguments
|
|
16
16
|
NO_ARGUMENT = Object.new
|
|
17
17
|
|
|
18
|
+
LOCK_ERR = "can't modify middleware stack after making a request"
|
|
19
|
+
MISSING_ADAPTER_ERROR = "An attempt to run a request with a Faraday::Connection without adapter has been made.\n" \
|
|
20
|
+
"Please set Faraday.default_adapter or provide one when initializing the connection.\n" \
|
|
21
|
+
'For more info, check https://lostisland.github.io/faraday/usage/.'
|
|
22
|
+
|
|
18
23
|
attr_accessor :handlers
|
|
19
24
|
|
|
20
25
|
# Error raised when trying to modify the stack after calling `lock!`
|
|
@@ -27,10 +32,11 @@ module Faraday
|
|
|
27
32
|
|
|
28
33
|
attr_reader :name
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
def initialize(klass, *args, **kwargs, &block)
|
|
31
36
|
@name = klass.to_s
|
|
32
37
|
REGISTRY.set(klass) if klass.respond_to?(:name)
|
|
33
38
|
@args = args
|
|
39
|
+
@kwargs = kwargs
|
|
34
40
|
@block = block
|
|
35
41
|
end
|
|
36
42
|
|
|
@@ -53,7 +59,7 @@ module Faraday
|
|
|
53
59
|
end
|
|
54
60
|
|
|
55
61
|
def build(app = nil)
|
|
56
|
-
klass.new(app, *@args, &@block)
|
|
62
|
+
klass.new(app, *@args, **@kwargs, &@block)
|
|
57
63
|
end
|
|
58
64
|
end
|
|
59
65
|
|
|
@@ -88,52 +94,52 @@ module Faraday
|
|
|
88
94
|
@handlers.frozen?
|
|
89
95
|
end
|
|
90
96
|
|
|
91
|
-
|
|
97
|
+
def use(klass, ...)
|
|
92
98
|
if klass.is_a? Symbol
|
|
93
|
-
use_symbol(Faraday::Middleware, klass,
|
|
99
|
+
use_symbol(Faraday::Middleware, klass, ...)
|
|
94
100
|
else
|
|
95
101
|
raise_if_locked
|
|
96
102
|
raise_if_adapter(klass)
|
|
97
|
-
@handlers << self.class::Handler.new(klass,
|
|
103
|
+
@handlers << self.class::Handler.new(klass, ...)
|
|
98
104
|
end
|
|
99
105
|
end
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
use_symbol(Faraday::Request, key,
|
|
107
|
+
def request(key, ...)
|
|
108
|
+
use_symbol(Faraday::Request, key, ...)
|
|
103
109
|
end
|
|
104
110
|
|
|
105
|
-
|
|
106
|
-
use_symbol(Faraday::Response,
|
|
111
|
+
def response(...)
|
|
112
|
+
use_symbol(Faraday::Response, ...)
|
|
107
113
|
end
|
|
108
114
|
|
|
109
|
-
|
|
115
|
+
def adapter(klass = NO_ARGUMENT, *args, **kwargs, &block)
|
|
110
116
|
return @adapter if klass == NO_ARGUMENT || klass.nil?
|
|
111
117
|
|
|
112
118
|
klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
|
|
113
|
-
@adapter = self.class::Handler.new(klass, *args, &block)
|
|
119
|
+
@adapter = self.class::Handler.new(klass, *args, **kwargs, &block)
|
|
114
120
|
end
|
|
115
121
|
|
|
116
122
|
## methods to push onto the various positions in the stack:
|
|
117
123
|
|
|
118
|
-
|
|
124
|
+
def insert(index, ...)
|
|
119
125
|
raise_if_locked
|
|
120
126
|
index = assert_index(index)
|
|
121
|
-
handler = self.class::Handler.new(
|
|
127
|
+
handler = self.class::Handler.new(...)
|
|
122
128
|
@handlers.insert(index, handler)
|
|
123
129
|
end
|
|
124
130
|
|
|
125
131
|
alias insert_before insert
|
|
126
132
|
|
|
127
|
-
|
|
133
|
+
def insert_after(index, ...)
|
|
128
134
|
index = assert_index(index)
|
|
129
|
-
insert(index + 1,
|
|
135
|
+
insert(index + 1, ...)
|
|
130
136
|
end
|
|
131
137
|
|
|
132
|
-
|
|
138
|
+
def swap(index, ...)
|
|
133
139
|
raise_if_locked
|
|
134
140
|
index = assert_index(index)
|
|
135
141
|
@handlers.delete_at(index)
|
|
136
|
-
insert(index,
|
|
142
|
+
insert(index, ...)
|
|
137
143
|
end
|
|
138
144
|
|
|
139
145
|
def delete(handler)
|
|
@@ -210,17 +216,12 @@ module Faraday
|
|
|
210
216
|
|
|
211
217
|
private
|
|
212
218
|
|
|
213
|
-
LOCK_ERR = "can't modify middleware stack after making a request"
|
|
214
|
-
MISSING_ADAPTER_ERROR = "An attempt to run a request with a Faraday::Connection without adapter has been made.\n" \
|
|
215
|
-
"Please set Faraday.default_adapter or provide one when initializing the connection.\n" \
|
|
216
|
-
'For more info, check https://lostisland.github.io/faraday/usage/.'
|
|
217
|
-
|
|
218
219
|
def raise_if_locked
|
|
219
220
|
raise StackLocked, LOCK_ERR if locked?
|
|
220
221
|
end
|
|
221
222
|
|
|
222
223
|
def raise_if_adapter(klass)
|
|
223
|
-
return unless
|
|
224
|
+
return unless klass <= Faraday::Adapter
|
|
224
225
|
|
|
225
226
|
raise 'Adapter should be set using the `adapter` method, not `use`'
|
|
226
227
|
end
|
|
@@ -233,12 +234,8 @@ module Faraday
|
|
|
233
234
|
!@adapter.nil?
|
|
234
235
|
end
|
|
235
236
|
|
|
236
|
-
def
|
|
237
|
-
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
ruby2_keywords def use_symbol(mod, key, *args, &block)
|
|
241
|
-
use(mod.lookup_middleware(key), *args, &block)
|
|
237
|
+
def use_symbol(mod, key, ...)
|
|
238
|
+
use(mod.lookup_middleware(key), ...)
|
|
242
239
|
end
|
|
243
240
|
|
|
244
241
|
def assert_index(index)
|
|
@@ -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)
|
|
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
|
|
|
@@ -15,7 +15,7 @@ module Faraday
|
|
|
15
15
|
404 => Faraday::ResourceNotFound,
|
|
16
16
|
408 => Faraday::RequestTimeoutError,
|
|
17
17
|
409 => Faraday::ConflictError,
|
|
18
|
-
422 => Faraday::
|
|
18
|
+
422 => Faraday::UnprocessableContentError,
|
|
19
19
|
429 => Faraday::TooManyRequestsError
|
|
20
20
|
}.freeze
|
|
21
21
|
# rubocop:enable Naming/ConstantName
|
data/lib/faraday/response.rb
CHANGED
|
@@ -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:
|
|
64
|
-
response_headers:
|
|
65
|
-
url:
|
|
67
|
+
status: status, body: body,
|
|
68
|
+
response_headers: headers,
|
|
69
|
+
url: url
|
|
66
70
|
}
|
|
67
71
|
end
|
|
68
72
|
|
|
@@ -51,7 +51,7 @@ module Faraday
|
|
|
51
51
|
|
|
52
52
|
def [](key)
|
|
53
53
|
key = KeyMap[key]
|
|
54
|
-
super
|
|
54
|
+
super || super(@names[key.downcase])
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def []=(key, val)
|
|
@@ -59,13 +59,13 @@ module Faraday
|
|
|
59
59
|
key = (@names[key.downcase] ||= key)
|
|
60
60
|
# join multiple values with a comma
|
|
61
61
|
val = val.to_ary.join(', ') if val.respond_to?(:to_ary)
|
|
62
|
-
super
|
|
62
|
+
super
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def fetch(key, ...)
|
|
66
66
|
key = KeyMap[key]
|
|
67
67
|
key = @names.fetch(key.downcase, key)
|
|
68
|
-
super
|
|
68
|
+
super
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
def delete(key)
|
|
@@ -74,13 +74,13 @@ module Faraday
|
|
|
74
74
|
return unless key
|
|
75
75
|
|
|
76
76
|
@names.delete key.downcase
|
|
77
|
-
super
|
|
77
|
+
super
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def dig(key, *rest)
|
|
81
81
|
key = KeyMap[key]
|
|
82
82
|
key = @names.fetch(key.downcase, key)
|
|
83
|
-
super
|
|
83
|
+
super
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def include?(key)
|
data/lib/faraday/version.rb
CHANGED
data/lib/faraday.rb
CHANGED
|
@@ -439,4 +439,22 @@ RSpec.describe Faraday::Adapter::Test do
|
|
|
439
439
|
end
|
|
440
440
|
end
|
|
441
441
|
end
|
|
442
|
+
|
|
443
|
+
describe '#clear' do
|
|
444
|
+
it 'removes pending stubs' do
|
|
445
|
+
stubs.clear
|
|
446
|
+
expect { connection.get('/hello') }.to raise_error(described_class::Stubs::NotFound)
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
it 'removes already consumed stubs' do
|
|
450
|
+
expect(connection.get('/hello').body).to eq('hello')
|
|
451
|
+
stubs.clear
|
|
452
|
+
expect { connection.get('/hello') }.to raise_error(described_class::Stubs::NotFound)
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
it 'leaves the stubs empty' do
|
|
456
|
+
stubs.clear
|
|
457
|
+
expect(stubs).to be_empty
|
|
458
|
+
end
|
|
459
|
+
end
|
|
442
460
|
end
|
|
@@ -311,6 +311,46 @@ RSpec.describe Faraday::Connection do
|
|
|
311
311
|
end
|
|
312
312
|
end
|
|
313
313
|
|
|
314
|
+
context 'with protocol-relative URL (GHSA-33mh-2634-fwr2)' do
|
|
315
|
+
it 'does not allow host override with //evil.com/path' do
|
|
316
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
317
|
+
uri = conn.build_exclusive_url('//evil.com/path')
|
|
318
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
it 'does not allow host override with URI("//evil.com/path")' do
|
|
322
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
323
|
+
uri = conn.build_exclusive_url(URI('//evil.com/path?token=1'))
|
|
324
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
325
|
+
expect(uri.query).to eq('token=1')
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
it 'does not allow host override with //evil.com:8080/path' do
|
|
329
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
330
|
+
uri = conn.build_exclusive_url('//evil.com:8080/path')
|
|
331
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
it 'does not allow host override with //user:pass@evil.com/path' do
|
|
335
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
336
|
+
uri = conn.build_exclusive_url('//user:pass@evil.com/path')
|
|
337
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
it 'does not allow host override with ///evil.com' do
|
|
341
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
342
|
+
uri = conn.build_exclusive_url('///evil.com')
|
|
343
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it 'still allows single-slash absolute paths' do
|
|
347
|
+
conn.url_prefix = 'http://httpbingo.org/api'
|
|
348
|
+
uri = conn.build_exclusive_url('/safe/path')
|
|
349
|
+
expect(uri.host).to eq('httpbingo.org')
|
|
350
|
+
expect(uri.path).to eq('/safe/path')
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
314
354
|
context 'with a custom `default_uri_parser`' do
|
|
315
355
|
let(:url) { 'http://httpbingo.org' }
|
|
316
356
|
let(:parser) { Addressable::URI }
|
|
@@ -340,6 +380,18 @@ RSpec.describe Faraday::Connection do
|
|
|
340
380
|
url = conn.build_url(nil, b: 2, c: 3)
|
|
341
381
|
expect(url.to_s).to eq('http://httpbingo.org/nigiri?a=1&b=2&c=3')
|
|
342
382
|
end
|
|
383
|
+
|
|
384
|
+
it 'raises a controlled error when URL query params exceed the nested depth limit' do
|
|
385
|
+
original_param_depth_limit = Faraday::NestedParamsEncoder.param_depth_limit
|
|
386
|
+
Faraday::NestedParamsEncoder.param_depth_limit = 2
|
|
387
|
+
|
|
388
|
+
expect { conn.build_url('/nigiri?a[b][c]=1') }.to raise_error(
|
|
389
|
+
Faraday::Error,
|
|
390
|
+
'exceeded nested parameter depth limit of 2'
|
|
391
|
+
)
|
|
392
|
+
ensure
|
|
393
|
+
Faraday::NestedParamsEncoder.param_depth_limit = original_param_depth_limit
|
|
394
|
+
end
|
|
343
395
|
end
|
|
344
396
|
|
|
345
397
|
describe '#build_request' do
|
data/spec/faraday/error_spec.rb
CHANGED
|
@@ -23,8 +23,12 @@ RSpec.describe Faraday::Error do
|
|
|
23
23
|
|
|
24
24
|
it { expect(subject.wrapped_exception).to be_nil }
|
|
25
25
|
it { expect(subject.response).to eq(exception) }
|
|
26
|
-
it { expect(subject.message).to eq('the server responded with status 400') }
|
|
27
|
-
|
|
26
|
+
it { expect(subject.message).to eq('the server responded with status 400 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
|
27
|
+
if RUBY_VERSION >= '3.4'
|
|
28
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
|
29
|
+
else
|
|
30
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
|
31
|
+
end
|
|
28
32
|
it { expect(subject.response_status).to eq(400) }
|
|
29
33
|
it { expect(subject.response_headers).to be_nil }
|
|
30
34
|
it { expect(subject.response_body).to be_nil }
|
|
@@ -61,7 +65,11 @@ RSpec.describe Faraday::Error do
|
|
|
61
65
|
it { expect(subject.wrapped_exception).to be_nil }
|
|
62
66
|
it { expect(subject.response).to eq(response) }
|
|
63
67
|
it { expect(subject.message).to eq('custom message') }
|
|
64
|
-
|
|
68
|
+
if RUBY_VERSION >= '3.4'
|
|
69
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
|
|
70
|
+
else
|
|
71
|
+
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
|
|
72
|
+
end
|
|
65
73
|
it { expect(subject.response_status).to eq(400) }
|
|
66
74
|
it { expect(subject.response_headers).to be_nil }
|
|
67
75
|
it { expect(subject.response_body).to be_nil }
|
|
@@ -81,5 +89,87 @@ RSpec.describe Faraday::Error do
|
|
|
81
89
|
it { expect(subject.response_headers).to eq(headers) }
|
|
82
90
|
it { expect(subject.response_body).to eq(body) }
|
|
83
91
|
end
|
|
92
|
+
|
|
93
|
+
context 'with hash missing status key' do
|
|
94
|
+
let(:exception) { { body: 'error body' } }
|
|
95
|
+
|
|
96
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
97
|
+
it { expect(subject.response).to eq(exception) }
|
|
98
|
+
it { expect(subject.message).to eq('the server responded with status - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
context 'with hash with status but missing request data' do
|
|
102
|
+
let(:exception) { { status: 404, body: 'not found' } } # missing request key
|
|
103
|
+
|
|
104
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
105
|
+
it { expect(subject.response).to eq(exception) }
|
|
106
|
+
it { expect(subject.message).to eq('the server responded with status 404 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
context 'with hash with status and request but missing method in request' do
|
|
110
|
+
let(:exception) { { status: 404, body: 'not found', request: { url: 'http://example.com/test' } } } # missing method
|
|
111
|
+
|
|
112
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
113
|
+
it { expect(subject.response).to eq(exception) }
|
|
114
|
+
it { expect(subject.message).to eq('the server responded with status 404 for http://example.com/test') }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
context 'with hash with status and request but missing url in request' do
|
|
118
|
+
let(:exception) { { status: 404, body: 'not found', request: { method: :get } } } # missing url
|
|
119
|
+
|
|
120
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
121
|
+
it { expect(subject.response).to eq(exception) }
|
|
122
|
+
it { expect(subject.message).to eq('the server responded with status 404 for GET ') }
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context 'with properly formed Faraday::Env' do
|
|
126
|
+
# This represents the normal case - a well-formed Faraday::Env object
|
|
127
|
+
# with all the standard properties populated as they would be during
|
|
128
|
+
# a typical HTTP request/response cycle
|
|
129
|
+
let(:exception) { Faraday::Env.new }
|
|
130
|
+
|
|
131
|
+
before do
|
|
132
|
+
exception.status = 500
|
|
133
|
+
exception.method = :post
|
|
134
|
+
exception.url = URI('https://api.example.com/users')
|
|
135
|
+
exception.request = Faraday::RequestOptions.new
|
|
136
|
+
exception.response_headers = { 'content-type' => 'application/json' }
|
|
137
|
+
exception.response_body = '{"error": "Internal server error"}'
|
|
138
|
+
exception.request_headers = { 'authorization' => 'Bearer token123' }
|
|
139
|
+
exception.request_body = '{"name": "John"}'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
143
|
+
it { expect(subject.response).to eq(exception) }
|
|
144
|
+
it { expect(subject.message).to eq('the server responded with status 500 for POST https://api.example.com/users') }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
context 'with Faraday::Env missing status key' do
|
|
148
|
+
let(:exception) { Faraday::Env.new }
|
|
149
|
+
|
|
150
|
+
before do
|
|
151
|
+
exception[:body] = 'error body'
|
|
152
|
+
# Intentionally not setting status
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
156
|
+
it { expect(subject.response).to eq(exception) }
|
|
157
|
+
it { expect(subject.message).to eq('the server responded with status for ') }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
context 'with Faraday::Env with direct method and url properties' do
|
|
161
|
+
let(:exception) { Faraday::Env.new }
|
|
162
|
+
|
|
163
|
+
before do
|
|
164
|
+
exception.status = 404
|
|
165
|
+
exception.method = :get
|
|
166
|
+
exception.url = URI('http://example.com/test')
|
|
167
|
+
exception[:body] = 'not found'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it { expect(subject.wrapped_exception).to be_nil }
|
|
171
|
+
it { expect(subject.response).to eq(exception) }
|
|
172
|
+
it { expect(subject.message).to eq('the server responded with status 404 for GET http://example.com/test') }
|
|
173
|
+
end
|
|
84
174
|
end
|
|
85
175
|
end
|
|
@@ -52,14 +52,14 @@ RSpec.describe Faraday::Middleware do
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
describe '#close' do
|
|
55
|
-
context "with app that doesn't support
|
|
55
|
+
context "with app that doesn't support #close" do
|
|
56
56
|
it 'should issue warning' do
|
|
57
57
|
is_expected.to receive(:warn)
|
|
58
58
|
subject.close
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
context
|
|
62
|
+
context 'with app that supports #close' do
|
|
63
63
|
it 'should issue warning' do
|
|
64
64
|
expect(app).to receive(:close)
|
|
65
65
|
is_expected.to_not receive(:warn)
|
|
@@ -27,6 +27,33 @@ RSpec.describe Faraday::ProxyOptions do
|
|
|
27
27
|
expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>')
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
it 'works with hash' do
|
|
31
|
+
hash = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
|
32
|
+
options = Faraday::ProxyOptions.from(hash)
|
|
33
|
+
expect(options.user).to eq('user')
|
|
34
|
+
expect(options.password).to eq('pass')
|
|
35
|
+
expect(options.uri).to be_a_kind_of(URI)
|
|
36
|
+
expect(options.path).to eq('')
|
|
37
|
+
expect(options.port).to eq(80)
|
|
38
|
+
expect(options.host).to eq('example.org')
|
|
39
|
+
expect(options.scheme).to eq('http')
|
|
40
|
+
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it 'works with option' do
|
|
44
|
+
opt_arg = { user: 'user', password: 'pass', uri: 'http://@example.org' }
|
|
45
|
+
option = Faraday::ConnectionOptions.from(proxy: opt_arg)
|
|
46
|
+
options = Faraday::ProxyOptions.from(option.proxy)
|
|
47
|
+
expect(options.user).to eq('user')
|
|
48
|
+
expect(options.password).to eq('pass')
|
|
49
|
+
expect(options.uri).to be_a_kind_of(URI)
|
|
50
|
+
expect(options.path).to eq('')
|
|
51
|
+
expect(options.port).to eq(80)
|
|
52
|
+
expect(options.host).to eq('example.org')
|
|
53
|
+
expect(options.scheme).to eq('http')
|
|
54
|
+
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
|
|
55
|
+
end
|
|
56
|
+
|
|
30
57
|
it 'works with no auth' do
|
|
31
58
|
proxy = Faraday::ProxyOptions.from 'http://example.org'
|
|
32
59
|
expect(proxy.user).to be_nil
|
|
@@ -5,6 +5,13 @@ require 'rack/utils'
|
|
|
5
5
|
RSpec.describe Faraday::NestedParamsEncoder do
|
|
6
6
|
it_behaves_like 'a params encoder'
|
|
7
7
|
|
|
8
|
+
around do |example|
|
|
9
|
+
original_param_depth_limit = described_class.param_depth_limit
|
|
10
|
+
example.run
|
|
11
|
+
ensure
|
|
12
|
+
described_class.param_depth_limit = original_param_depth_limit
|
|
13
|
+
end
|
|
14
|
+
|
|
8
15
|
it 'decodes arrays' do
|
|
9
16
|
query = 'a[1]=one&a[2]=two&a[3]=three'
|
|
10
17
|
expected = { 'a' => %w[one two three] }
|
|
@@ -53,6 +60,27 @@ RSpec.describe Faraday::NestedParamsEncoder do
|
|
|
53
60
|
expect(subject.decode(query)).to eq(expected)
|
|
54
61
|
end
|
|
55
62
|
|
|
63
|
+
it 'allows nested params within the configured depth limit' do
|
|
64
|
+
described_class.param_depth_limit = 3
|
|
65
|
+
|
|
66
|
+
expect(subject.decode('a[b][c]=1')).to eq({ 'a' => { 'b' => { 'c' => '1' } } })
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'raises a controlled error when nested params exceed the depth limit' do
|
|
70
|
+
described_class.param_depth_limit = 2
|
|
71
|
+
|
|
72
|
+
expect { subject.decode('a[b][c]=1') }.to raise_error(
|
|
73
|
+
Faraday::Error,
|
|
74
|
+
'exceeded nested parameter depth limit of 2'
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it 'allows disabling the nested params depth limit' do
|
|
79
|
+
described_class.param_depth_limit = nil
|
|
80
|
+
|
|
81
|
+
expect(subject.decode('a[b][c][d]=1')).to eq({ 'a' => { 'b' => { 'c' => { 'd' => '1' } } } })
|
|
82
|
+
end
|
|
83
|
+
|
|
56
84
|
it 'decodes nested final value overrides any type' do
|
|
57
85
|
query = 'a[b][c]=1&a[b]=2'
|
|
58
86
|
expected = { 'a' => { 'b' => '2' } }
|
|
@@ -31,6 +31,15 @@ RSpec.describe Faraday::Request do
|
|
|
31
31
|
it { expect(subject.to_env(conn).url.to_s).to eq('http://httpbingo.org/api/foo.json?a=1') }
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
context 'when setting the url on setup with a protocol-relative URI' do
|
|
35
|
+
let(:block) { proc { |req| req.url URI.parse('//evil.com/path?token=1') } }
|
|
36
|
+
let(:url) { subject.to_env(conn).url }
|
|
37
|
+
|
|
38
|
+
it { expect(url.host).to eq('httpbingo.org') }
|
|
39
|
+
it { expect(url.path).to eq('/api///evil.com/path') }
|
|
40
|
+
it { expect(url.query).to eq('token=1') }
|
|
41
|
+
end
|
|
42
|
+
|
|
34
43
|
context 'when setting the url on setup with a string path and params' do
|
|
35
44
|
let(:block) { proc { |req| req.url 'foo.json', 'a' => 1 } }
|
|
36
45
|
|
|
@@ -106,7 +106,7 @@ RSpec.describe Faraday::Response::Json, type: :response do
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
it 'passes relevant options to JSON parse' do
|
|
109
|
-
expect(
|
|
109
|
+
expect(JSON).to receive(:parse)
|
|
110
110
|
.with(body, options[:parser_options])
|
|
111
111
|
.and_return(result)
|
|
112
112
|
|
|
@@ -21,6 +21,7 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
21
21
|
stubs.post('/ohai') { [200, { 'Content-Type' => 'text/html' }, 'fred'] }
|
|
22
22
|
stubs.post('/ohyes') { [200, { 'Content-Type' => 'text/html' }, 'pebbles'] }
|
|
23
23
|
stubs.get('/rubbles') { [200, { 'Content-Type' => 'application/json' }, rubbles] }
|
|
24
|
+
stubs.get('/8bit') { [200, { 'Content-Type' => 'text/html' }, (+'café!').force_encoding(Encoding::ASCII_8BIT)] }
|
|
24
25
|
stubs.get('/filtered_body') { [200, { 'Content-Type' => 'text/html' }, 'soylent green is people'] }
|
|
25
26
|
stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] }
|
|
26
27
|
stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] }
|
|
@@ -55,6 +56,26 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
55
56
|
end
|
|
56
57
|
end
|
|
57
58
|
|
|
59
|
+
context 'when logger with program name' do
|
|
60
|
+
let(:logger) { Logger.new(string_io, progname: 'my_best_program') }
|
|
61
|
+
|
|
62
|
+
it 'logs with program name' do
|
|
63
|
+
conn.get '/hello'
|
|
64
|
+
|
|
65
|
+
expect(string_io.string).to match('-- my_best_program: request:')
|
|
66
|
+
expect(string_io.string).to match('-- my_best_program: response:')
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
context 'when logger without program name' do
|
|
71
|
+
it 'logs without program name' do
|
|
72
|
+
conn.get '/hello'
|
|
73
|
+
|
|
74
|
+
expect(string_io.string).to match('-- : request:')
|
|
75
|
+
expect(string_io.string).to match('-- : response:')
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
58
79
|
context 'with default formatter' do
|
|
59
80
|
let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
|
|
60
81
|
|
|
@@ -169,7 +190,7 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
169
190
|
context 'when logging request body' do
|
|
170
191
|
let(:logger_options) { { bodies: { request: true } } }
|
|
171
192
|
|
|
172
|
-
it '
|
|
193
|
+
it 'logs only request body' do
|
|
173
194
|
conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
|
|
174
195
|
expect(string_io.string).to match(%(name=Tamago))
|
|
175
196
|
expect(string_io.string).not_to match(%(pebbles))
|
|
@@ -179,7 +200,7 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
179
200
|
context 'when logging response body' do
|
|
180
201
|
let(:logger_options) { { bodies: { response: true } } }
|
|
181
202
|
|
|
182
|
-
it '
|
|
203
|
+
it 'logs only response body' do
|
|
183
204
|
conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
|
|
184
205
|
expect(string_io.string).to match(%(pebbles))
|
|
185
206
|
expect(string_io.string).not_to match(%(name=Hamachi))
|
|
@@ -189,15 +210,15 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
189
210
|
context 'when logging request and response bodies' do
|
|
190
211
|
let(:logger_options) { { bodies: true } }
|
|
191
212
|
|
|
192
|
-
it '
|
|
213
|
+
it 'logs request and response body' do
|
|
193
214
|
conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
|
|
194
215
|
expect(string_io.string).to match(%(name=Ebi))
|
|
195
216
|
expect(string_io.string).to match(%(pebbles))
|
|
196
217
|
end
|
|
197
218
|
|
|
198
|
-
it '
|
|
219
|
+
it 'logs response body object' do
|
|
199
220
|
conn.get '/rubbles', nil, accept: 'text/html'
|
|
200
|
-
expect(string_io.string).to match(%([
|
|
221
|
+
expect(string_io.string).to match(%(["Barney", "Betty", "Bam Bam"]\n))
|
|
201
222
|
end
|
|
202
223
|
|
|
203
224
|
it 'logs filter body' do
|
|
@@ -208,6 +229,26 @@ RSpec.describe Faraday::Response::Logger do
|
|
|
208
229
|
end
|
|
209
230
|
end
|
|
210
231
|
|
|
232
|
+
context 'when bodies are logged by default' do
|
|
233
|
+
before do
|
|
234
|
+
described_class.default_options = { bodies: true }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
it 'logs response body' do
|
|
238
|
+
conn.post '/ohai'
|
|
239
|
+
expect(string_io.string).to match(%(fred))
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
it 'converts to UTF-8' do
|
|
243
|
+
conn.get '/8bit'
|
|
244
|
+
expect(string_io.string).to match(%(caf��!))
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
after do
|
|
248
|
+
described_class.default_options = { bodies: false }
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
211
252
|
context 'when logging errors' do
|
|
212
253
|
let(:logger_options) { { errors: true } }
|
|
213
254
|
|
|
@@ -13,7 +13,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
13
13
|
stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
14
14
|
stub.get('request-timeout') { [408, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
15
15
|
stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
16
|
-
stub.get('unprocessable-
|
|
16
|
+
stub.get('unprocessable-content') { [422, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
17
17
|
stub.get('too-many-requests') { [429, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
18
18
|
stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] }
|
|
19
19
|
stub.get('nil-status') { [nil, { 'X-Reason' => 'nil' }, 'fail'] }
|
|
@@ -28,7 +28,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
28
28
|
|
|
29
29
|
it 'raises Faraday::BadRequestError for 400 responses' do
|
|
30
30
|
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
|
|
31
|
-
expect(ex.message).to eq('the server responded with status 400')
|
|
31
|
+
expect(ex.message).to eq('the server responded with status 400 for GET http:/bad-request')
|
|
32
32
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
33
33
|
expect(ex.response[:status]).to eq(400)
|
|
34
34
|
expect(ex.response_status).to eq(400)
|
|
@@ -39,7 +39,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
39
39
|
|
|
40
40
|
it 'raises Faraday::UnauthorizedError for 401 responses' do
|
|
41
41
|
expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
|
|
42
|
-
expect(ex.message).to eq('the server responded with status 401')
|
|
42
|
+
expect(ex.message).to eq('the server responded with status 401 for GET http:/unauthorized')
|
|
43
43
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
44
44
|
expect(ex.response[:status]).to eq(401)
|
|
45
45
|
expect(ex.response_status).to eq(401)
|
|
@@ -50,7 +50,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
50
50
|
|
|
51
51
|
it 'raises Faraday::ForbiddenError for 403 responses' do
|
|
52
52
|
expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
|
|
53
|
-
expect(ex.message).to eq('the server responded with status 403')
|
|
53
|
+
expect(ex.message).to eq('the server responded with status 403 for GET http:/forbidden')
|
|
54
54
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
55
55
|
expect(ex.response[:status]).to eq(403)
|
|
56
56
|
expect(ex.response_status).to eq(403)
|
|
@@ -61,7 +61,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
61
61
|
|
|
62
62
|
it 'raises Faraday::ResourceNotFound for 404 responses' do
|
|
63
63
|
expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
|
|
64
|
-
expect(ex.message).to eq('the server responded with status 404')
|
|
64
|
+
expect(ex.message).to eq('the server responded with status 404 for GET http:/not-found')
|
|
65
65
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
66
66
|
expect(ex.response[:status]).to eq(404)
|
|
67
67
|
expect(ex.response_status).to eq(404)
|
|
@@ -83,7 +83,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
83
83
|
|
|
84
84
|
it 'raises Faraday::RequestTimeoutError for 408 responses' do
|
|
85
85
|
expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex|
|
|
86
|
-
expect(ex.message).to eq('the server responded with status 408')
|
|
86
|
+
expect(ex.message).to eq('the server responded with status 408 for GET http:/request-timeout')
|
|
87
87
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
88
88
|
expect(ex.response[:status]).to eq(408)
|
|
89
89
|
expect(ex.response_status).to eq(408)
|
|
@@ -94,7 +94,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
94
94
|
|
|
95
95
|
it 'raises Faraday::ConflictError for 409 responses' do
|
|
96
96
|
expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
|
|
97
|
-
expect(ex.message).to eq('the server responded with status 409')
|
|
97
|
+
expect(ex.message).to eq('the server responded with status 409 for GET http:/conflict')
|
|
98
98
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
99
99
|
expect(ex.response[:status]).to eq(409)
|
|
100
100
|
expect(ex.response_status).to eq(409)
|
|
@@ -103,9 +103,20 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
103
103
|
end
|
|
104
104
|
end
|
|
105
105
|
|
|
106
|
-
it 'raises Faraday::UnprocessableEntityError for 422 responses' do
|
|
107
|
-
expect { conn.get('unprocessable-
|
|
108
|
-
expect(ex.message).to eq('the server responded with status 422')
|
|
106
|
+
it 'raises legacy Faraday::UnprocessableEntityError for 422 responses' do
|
|
107
|
+
expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
|
|
108
|
+
expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content')
|
|
109
|
+
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
110
|
+
expect(ex.response[:status]).to eq(422)
|
|
111
|
+
expect(ex.response_status).to eq(422)
|
|
112
|
+
expect(ex.response_body).to eq('keep looking')
|
|
113
|
+
expect(ex.response_headers['X-Reason']).to eq('because')
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'raises Faraday::UnprocessableContentError for 422 responses' do
|
|
118
|
+
expect { conn.get('unprocessable-content') }.to raise_error(Faraday::UnprocessableContentError) do |ex|
|
|
119
|
+
expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-content')
|
|
109
120
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
110
121
|
expect(ex.response[:status]).to eq(422)
|
|
111
122
|
expect(ex.response_status).to eq(422)
|
|
@@ -116,7 +127,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
116
127
|
|
|
117
128
|
it 'raises Faraday::TooManyRequestsError for 429 responses' do
|
|
118
129
|
expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex|
|
|
119
|
-
expect(ex.message).to eq('the server responded with status 429')
|
|
130
|
+
expect(ex.message).to eq('the server responded with status 429 for GET http:/too-many-requests')
|
|
120
131
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
121
132
|
expect(ex.response[:status]).to eq(429)
|
|
122
133
|
expect(ex.response_status).to eq(429)
|
|
@@ -138,7 +149,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
138
149
|
|
|
139
150
|
it 'raises Faraday::ClientError for other 4xx responses' do
|
|
140
151
|
expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
|
|
141
|
-
expect(ex.message).to eq('the server responded with status 499')
|
|
152
|
+
expect(ex.message).to eq('the server responded with status 499 for GET http:/4xx')
|
|
142
153
|
expect(ex.response[:headers]['X-Reason']).to eq('because')
|
|
143
154
|
expect(ex.response[:status]).to eq(499)
|
|
144
155
|
expect(ex.response_status).to eq(499)
|
|
@@ -149,7 +160,7 @@ RSpec.describe Faraday::Response::RaiseError do
|
|
|
149
160
|
|
|
150
161
|
it 'raises Faraday::ServerError for 500 responses' do
|
|
151
162
|
expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
|
|
152
|
-
expect(ex.message).to eq('the server responded with status 500')
|
|
163
|
+
expect(ex.message).to eq('the server responded with status 500 for GET http:/server-error')
|
|
153
164
|
expect(ex.response[:headers]['X-Error']).to eq('bailout')
|
|
154
165
|
expect(ex.response[:status]).to eq(500)
|
|
155
166
|
expect(ex.response_status).to eq(500)
|
|
@@ -13,6 +13,7 @@ RSpec.describe Faraday::Response do
|
|
|
13
13
|
it { expect(subject.success?).to be_falsey }
|
|
14
14
|
it { expect(subject.status).to eq(404) }
|
|
15
15
|
it { expect(subject.body).to eq('yikes') }
|
|
16
|
+
it { expect(subject.url).to eq(URI('https://lostisland.github.io/faraday')) }
|
|
16
17
|
it { expect(subject.headers['Content-Type']).to eq('text/plain') }
|
|
17
18
|
it { expect(subject['content-type']).to eq('text/plain') }
|
|
18
19
|
|
|
@@ -31,6 +32,12 @@ RSpec.describe Faraday::Response do
|
|
|
31
32
|
it { expect(hash[:response_headers]).to eq(subject.headers) }
|
|
32
33
|
it { expect(hash[:body]).to eq(subject.body) }
|
|
33
34
|
it { expect(hash[:url]).to eq(subject.env.url) }
|
|
35
|
+
|
|
36
|
+
context 'when response is not finished' do
|
|
37
|
+
subject { Faraday::Response.new.to_hash }
|
|
38
|
+
|
|
39
|
+
it { is_expected.to eq({ status: nil, body: nil, response_headers: {}, url: nil }) }
|
|
40
|
+
end
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
describe 'marshal serialization support' do
|
data/spec/faraday/utils_spec.rb
CHANGED
|
@@ -33,7 +33,7 @@ RSpec.describe Faraday::Utils do
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it 'parses with URI' do
|
|
36
|
-
with_default_uri_parser(
|
|
36
|
+
with_default_uri_parser(URI) do
|
|
37
37
|
uri = normalize(url)
|
|
38
38
|
expect(uri.host).to eq('example.com')
|
|
39
39
|
end
|
|
@@ -104,6 +104,7 @@ RSpec.describe Faraday::Utils do
|
|
|
104
104
|
min_version: nil,
|
|
105
105
|
max_version: nil,
|
|
106
106
|
verify_hostname: nil,
|
|
107
|
+
hostname: nil,
|
|
107
108
|
ciphers: nil
|
|
108
109
|
}
|
|
109
110
|
end
|
metadata
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: faraday
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.14.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- "@technoweenie"
|
|
8
8
|
- "@iMacTia"
|
|
9
9
|
- "@olleolleolle"
|
|
10
|
-
autorequire:
|
|
11
10
|
bindir: bin
|
|
12
11
|
cert_chain: []
|
|
13
|
-
date:
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
14
13
|
dependencies:
|
|
15
14
|
- !ruby/object:Gem::Dependency
|
|
16
15
|
name: faraday-net_http
|
|
@@ -21,7 +20,7 @@ dependencies:
|
|
|
21
20
|
version: '2.0'
|
|
22
21
|
- - "<"
|
|
23
22
|
- !ruby/object:Gem::Version
|
|
24
|
-
version: '3.
|
|
23
|
+
version: '3.5'
|
|
25
24
|
type: :runtime
|
|
26
25
|
prerelease: false
|
|
27
26
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -31,7 +30,7 @@ dependencies:
|
|
|
31
30
|
version: '2.0'
|
|
32
31
|
- - "<"
|
|
33
32
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '3.
|
|
33
|
+
version: '3.5'
|
|
35
34
|
- !ruby/object:Gem::Dependency
|
|
36
35
|
name: json
|
|
37
36
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -60,7 +59,6 @@ dependencies:
|
|
|
60
59
|
- - ">="
|
|
61
60
|
- !ruby/object:Gem::Version
|
|
62
61
|
version: '0'
|
|
63
|
-
description:
|
|
64
62
|
email: technoweenie@gmail.com
|
|
65
63
|
executables: []
|
|
66
64
|
extensions: []
|
|
@@ -146,11 +144,10 @@ licenses:
|
|
|
146
144
|
- MIT
|
|
147
145
|
metadata:
|
|
148
146
|
homepage_uri: https://lostisland.github.io/faraday
|
|
149
|
-
changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.
|
|
147
|
+
changelog_uri: https://github.com/lostisland/faraday/releases/tag/v2.14.3
|
|
150
148
|
source_code_uri: https://github.com/lostisland/faraday
|
|
151
149
|
bug_tracker_uri: https://github.com/lostisland/faraday/issues
|
|
152
150
|
rubygems_mfa_required: 'true'
|
|
153
|
-
post_install_message:
|
|
154
151
|
rdoc_options: []
|
|
155
152
|
require_paths:
|
|
156
153
|
- lib
|
|
@@ -166,8 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
166
163
|
- !ruby/object:Gem::Version
|
|
167
164
|
version: '0'
|
|
168
165
|
requirements: []
|
|
169
|
-
rubygems_version: 3.
|
|
170
|
-
signing_key:
|
|
166
|
+
rubygems_version: 3.6.9
|
|
171
167
|
specification_version: 4
|
|
172
168
|
summary: HTTP/REST API client library.
|
|
173
169
|
test_files: []
|