faraday 0.13.0 → 2.0.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +496 -0
- data/LICENSE.md +1 -1
- data/README.md +28 -328
- data/Rakefile +7 -0
- data/examples/client_spec.rb +97 -0
- data/examples/client_test.rb +118 -0
- data/lib/faraday/adapter/test.rb +127 -68
- data/lib/faraday/adapter.rb +71 -22
- data/lib/faraday/adapter_registry.rb +30 -0
- data/lib/faraday/connection.rb +314 -226
- data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
- data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
- data/lib/faraday/error.rb +121 -37
- data/lib/faraday/logging/formatter.rb +106 -0
- data/lib/faraday/methods.rb +6 -0
- data/lib/faraday/middleware.rb +18 -25
- data/lib/faraday/middleware_registry.rb +65 -0
- data/lib/faraday/options/connection_options.rb +22 -0
- data/lib/faraday/options/env.rb +181 -0
- data/lib/faraday/options/proxy_options.rb +32 -0
- data/lib/faraday/options/request_options.rb +22 -0
- data/lib/faraday/options/ssl_options.rb +59 -0
- data/lib/faraday/options.rb +41 -195
- data/lib/faraday/parameters.rb +4 -196
- data/lib/faraday/rack_builder.rb +91 -74
- data/lib/faraday/request/authorization.rb +37 -29
- data/lib/faraday/request/instrumentation.rb +47 -27
- data/lib/faraday/request/json.rb +55 -0
- data/lib/faraday/request/url_encoded.rb +45 -23
- data/lib/faraday/request.rb +74 -32
- data/lib/faraday/response/json.rb +54 -0
- data/lib/faraday/response/logger.rb +22 -69
- data/lib/faraday/response/raise_error.rb +57 -14
- data/lib/faraday/response.rb +26 -33
- data/lib/faraday/utils/headers.rb +139 -0
- data/lib/faraday/utils/params_hash.rb +61 -0
- data/lib/faraday/utils.rb +47 -251
- data/lib/faraday/version.rb +5 -0
- data/lib/faraday.rb +104 -197
- data/spec/external_adapters/faraday_specs_setup.rb +14 -0
- data/spec/faraday/adapter/test_spec.rb +377 -0
- data/spec/faraday/adapter_registry_spec.rb +28 -0
- data/spec/faraday/adapter_spec.rb +55 -0
- data/spec/faraday/connection_spec.rb +787 -0
- data/spec/faraday/error_spec.rb +60 -0
- data/spec/faraday/middleware_spec.rb +52 -0
- data/spec/faraday/options/env_spec.rb +70 -0
- data/spec/faraday/options/options_spec.rb +297 -0
- data/spec/faraday/options/proxy_options_spec.rb +44 -0
- data/spec/faraday/options/request_options_spec.rb +19 -0
- data/spec/faraday/params_encoders/flat_spec.rb +42 -0
- data/spec/faraday/params_encoders/nested_spec.rb +142 -0
- data/spec/faraday/rack_builder_spec.rb +302 -0
- data/spec/faraday/request/authorization_spec.rb +83 -0
- data/spec/faraday/request/instrumentation_spec.rb +74 -0
- data/spec/faraday/request/json_spec.rb +111 -0
- data/spec/faraday/request/url_encoded_spec.rb +82 -0
- data/spec/faraday/request_spec.rb +109 -0
- data/spec/faraday/response/json_spec.rb +117 -0
- data/spec/faraday/response/logger_spec.rb +220 -0
- data/spec/faraday/response/raise_error_spec.rb +172 -0
- data/spec/faraday/response_spec.rb +75 -0
- data/spec/faraday/utils/headers_spec.rb +82 -0
- data/spec/faraday/utils_spec.rb +117 -0
- data/spec/faraday_spec.rb +37 -0
- data/spec/spec_helper.rb +132 -0
- data/spec/support/disabling_stub.rb +14 -0
- data/spec/support/fake_safe_buffer.rb +15 -0
- data/spec/support/helper_methods.rb +96 -0
- data/spec/support/shared_examples/adapter.rb +104 -0
- data/spec/support/shared_examples/params_encoder.rb +18 -0
- data/spec/support/shared_examples/request_method.rb +249 -0
- data/spec/support/streaming_response_checker.rb +35 -0
- metadata +71 -34
- data/lib/faraday/adapter/em_http.rb +0 -243
- data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -56
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -66
- data/lib/faraday/adapter/em_synchrony.rb +0 -106
- data/lib/faraday/adapter/excon.rb +0 -80
- data/lib/faraday/adapter/httpclient.rb +0 -128
- data/lib/faraday/adapter/net_http.rb +0 -135
- data/lib/faraday/adapter/net_http_persistent.rb +0 -54
- data/lib/faraday/adapter/patron.rb +0 -83
- data/lib/faraday/adapter/rack.rb +0 -58
- data/lib/faraday/adapter/typhoeus.rb +0 -123
- data/lib/faraday/autoload.rb +0 -84
- data/lib/faraday/request/basic_authentication.rb +0 -13
- data/lib/faraday/request/multipart.rb +0 -68
- data/lib/faraday/request/retry.rb +0 -164
- data/lib/faraday/request/token_authentication.rb +0 -15
- data/lib/faraday/upload_io.rb +0 -67
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
# FlatParamsEncoder manages URI params as a flat hash. Any Array values repeat
|
5
|
+
# the parameter multiple times.
|
6
|
+
module FlatParamsEncoder
|
7
|
+
class << self
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :'Faraday::Utils', :escape, :unescape
|
10
|
+
end
|
11
|
+
|
12
|
+
# Encode converts the given param into a URI querystring. Keys and values
|
13
|
+
# will converted to strings and appropriately escaped for the URI.
|
14
|
+
#
|
15
|
+
# @param params [Hash] query arguments to convert.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
#
|
19
|
+
# encode({a: %w[one two three], b: true, c: "C"})
|
20
|
+
# # => 'a=one&a=two&a=three&b=true&c=C'
|
21
|
+
#
|
22
|
+
# @return [String] the URI querystring (without the leading '?')
|
23
|
+
def self.encode(params)
|
24
|
+
return nil if params.nil?
|
25
|
+
|
26
|
+
unless params.is_a?(Array)
|
27
|
+
unless params.respond_to?(:to_hash)
|
28
|
+
raise TypeError,
|
29
|
+
"Can't convert #{params.class} into Hash."
|
30
|
+
end
|
31
|
+
params = params.to_hash
|
32
|
+
params = params.map do |key, value|
|
33
|
+
key = key.to_s if key.is_a?(Symbol)
|
34
|
+
[key, value]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Only to be used for non-Array inputs. Arrays should preserve order.
|
38
|
+
params.sort! if @sort_params
|
39
|
+
end
|
40
|
+
|
41
|
+
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
42
|
+
buffer = +''
|
43
|
+
params.each do |key, value|
|
44
|
+
encoded_key = escape(key)
|
45
|
+
if value.nil?
|
46
|
+
buffer << "#{encoded_key}&"
|
47
|
+
elsif value.is_a?(Array)
|
48
|
+
if value.empty?
|
49
|
+
buffer << "#{encoded_key}=&"
|
50
|
+
else
|
51
|
+
value.each do |sub_value|
|
52
|
+
encoded_value = escape(sub_value)
|
53
|
+
buffer << "#{encoded_key}=#{encoded_value}&"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
else
|
57
|
+
encoded_value = escape(value)
|
58
|
+
buffer << "#{encoded_key}=#{encoded_value}&"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
buffer.chop
|
62
|
+
end
|
63
|
+
|
64
|
+
# Decode converts the given URI querystring into a hash.
|
65
|
+
#
|
66
|
+
# @param query [String] query arguments to parse.
|
67
|
+
#
|
68
|
+
# @example
|
69
|
+
#
|
70
|
+
# decode('a=one&a=two&a=three&b=true&c=C')
|
71
|
+
# # => {"a"=>["one", "two", "three"], "b"=>"true", "c"=>"C"}
|
72
|
+
#
|
73
|
+
# @return [Hash] parsed keys and value strings from the querystring.
|
74
|
+
def self.decode(query)
|
75
|
+
return nil if query.nil?
|
76
|
+
|
77
|
+
empty_accumulator = {}
|
78
|
+
|
79
|
+
split_query = (query.split('&').map do |pair|
|
80
|
+
pair.split('=', 2) if pair && !pair.empty?
|
81
|
+
end).compact
|
82
|
+
split_query.each_with_object(empty_accumulator.dup) do |pair, accu|
|
83
|
+
pair[0] = unescape(pair[0])
|
84
|
+
pair[1] = true if pair[1].nil?
|
85
|
+
if pair[1].respond_to?(:to_str)
|
86
|
+
pair[1] = unescape(pair[1].to_str.tr('+', ' '))
|
87
|
+
end
|
88
|
+
if accu[pair[0]].is_a?(Array)
|
89
|
+
accu[pair[0]] << pair[1]
|
90
|
+
elsif accu[pair[0]]
|
91
|
+
accu[pair[0]] = [accu[pair[0]], pair[1]]
|
92
|
+
else
|
93
|
+
accu[pair[0]] = pair[1]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class << self
|
99
|
+
attr_accessor :sort_params
|
100
|
+
end
|
101
|
+
|
102
|
+
# Useful default for OAuth and caching.
|
103
|
+
@sort_params = true
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
# Sub-module for encoding parameters into query-string.
|
5
|
+
module EncodeMethods
|
6
|
+
# @param params [nil, Array, #to_hash] parameters to be encoded
|
7
|
+
#
|
8
|
+
# @return [String] the encoded params
|
9
|
+
#
|
10
|
+
# @raise [TypeError] if params can not be converted to a Hash
|
11
|
+
def encode(params)
|
12
|
+
return nil if params.nil?
|
13
|
+
|
14
|
+
unless params.is_a?(Array)
|
15
|
+
unless params.respond_to?(:to_hash)
|
16
|
+
raise TypeError, "Can't convert #{params.class} into Hash."
|
17
|
+
end
|
18
|
+
|
19
|
+
params = params.to_hash
|
20
|
+
params = params.map do |key, value|
|
21
|
+
key = key.to_s if key.is_a?(Symbol)
|
22
|
+
[key, value]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Only to be used for non-Array inputs. Arrays should preserve order.
|
26
|
+
params.sort! if @sort_params
|
27
|
+
end
|
28
|
+
|
29
|
+
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
30
|
+
buffer = +''
|
31
|
+
params.each do |parent, value|
|
32
|
+
encoded_parent = escape(parent)
|
33
|
+
buffer << "#{encode_pair(encoded_parent, value)}&"
|
34
|
+
end
|
35
|
+
buffer.chop
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def encode_pair(parent, value)
|
41
|
+
if value.is_a?(Hash)
|
42
|
+
encode_hash(parent, value)
|
43
|
+
elsif value.is_a?(Array)
|
44
|
+
encode_array(parent, value)
|
45
|
+
elsif value.nil?
|
46
|
+
parent
|
47
|
+
else
|
48
|
+
encoded_value = escape(value)
|
49
|
+
"#{parent}=#{encoded_value}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def encode_hash(parent, value)
|
54
|
+
value = value.map { |key, val| [escape(key), val] }.sort
|
55
|
+
|
56
|
+
buffer = +''
|
57
|
+
value.each do |key, val|
|
58
|
+
new_parent = "#{parent}%5B#{key}%5D"
|
59
|
+
buffer << "#{encode_pair(new_parent, val)}&"
|
60
|
+
end
|
61
|
+
buffer.chop
|
62
|
+
end
|
63
|
+
|
64
|
+
def encode_array(parent, value)
|
65
|
+
new_parent = "#{parent}%5B%5D"
|
66
|
+
return new_parent if value.empty?
|
67
|
+
|
68
|
+
buffer = +''
|
69
|
+
value.each { |val| buffer << "#{encode_pair(new_parent, val)}&" }
|
70
|
+
buffer.chop
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sub-module for decoding query-string into parameters.
|
75
|
+
module DecodeMethods
|
76
|
+
# @param query [nil, String]
|
77
|
+
#
|
78
|
+
# @return [Array<Array, String>] the decoded params
|
79
|
+
#
|
80
|
+
# @raise [TypeError] if the nesting is incorrect
|
81
|
+
def decode(query)
|
82
|
+
return nil if query.nil?
|
83
|
+
|
84
|
+
params = {}
|
85
|
+
query.split('&').each do |pair|
|
86
|
+
next if pair.empty?
|
87
|
+
|
88
|
+
key, value = pair.split('=', 2)
|
89
|
+
key = unescape(key)
|
90
|
+
value = unescape(value.tr('+', ' ')) if value
|
91
|
+
decode_pair(key, value, params)
|
92
|
+
end
|
93
|
+
|
94
|
+
dehash(params, 0)
|
95
|
+
end
|
96
|
+
|
97
|
+
protected
|
98
|
+
|
99
|
+
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/.freeze
|
100
|
+
|
101
|
+
def decode_pair(key, value, context)
|
102
|
+
subkeys = key.scan(SUBKEYS_REGEX)
|
103
|
+
subkeys.each_with_index do |subkey, i|
|
104
|
+
is_array = subkey =~ /[\[\]]+\Z/
|
105
|
+
subkey = Regexp.last_match.pre_match if is_array
|
106
|
+
last_subkey = i == subkeys.length - 1
|
107
|
+
|
108
|
+
context = prepare_context(context, subkey, is_array, last_subkey)
|
109
|
+
add_to_context(is_array, context, value, subkey) if last_subkey
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def prepare_context(context, subkey, is_array, last_subkey)
|
114
|
+
if !last_subkey || is_array
|
115
|
+
context = new_context(subkey, is_array, context)
|
116
|
+
end
|
117
|
+
if context.is_a?(Array) && !is_array
|
118
|
+
context = match_context(context, subkey)
|
119
|
+
end
|
120
|
+
context
|
121
|
+
end
|
122
|
+
|
123
|
+
def new_context(subkey, is_array, context)
|
124
|
+
value_type = is_array ? Array : Hash
|
125
|
+
if context[subkey] && !context[subkey].is_a?(value_type)
|
126
|
+
raise TypeError, "expected #{value_type.name} " \
|
127
|
+
"(got #{context[subkey].class.name}) for param `#{subkey}'"
|
128
|
+
end
|
129
|
+
|
130
|
+
context[subkey] ||= value_type.new
|
131
|
+
end
|
132
|
+
|
133
|
+
def match_context(context, subkey)
|
134
|
+
context << {} if !context.last.is_a?(Hash) || context.last.key?(subkey)
|
135
|
+
context.last
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_to_context(is_array, context, value, subkey)
|
139
|
+
is_array ? context << value : context[subkey] = value
|
140
|
+
end
|
141
|
+
|
142
|
+
# Internal: convert a nested hash with purely numeric keys into an array.
|
143
|
+
# FIXME: this is not compatible with Rack::Utils.parse_nested_query
|
144
|
+
# @!visibility private
|
145
|
+
def dehash(hash, depth)
|
146
|
+
hash.each do |key, value|
|
147
|
+
hash[key] = dehash(value, depth + 1) if value.is_a?(Hash)
|
148
|
+
end
|
149
|
+
|
150
|
+
if depth.positive? && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ }
|
151
|
+
hash.sort.map(&:last)
|
152
|
+
else
|
153
|
+
hash
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# This is the default encoder for Faraday requests.
|
159
|
+
# Using this encoder, parameters will be encoded respecting their structure,
|
160
|
+
# so you can send objects such as Arrays or Hashes as parameters
|
161
|
+
# for your requests.
|
162
|
+
module NestedParamsEncoder
|
163
|
+
class << self
|
164
|
+
attr_accessor :sort_params
|
165
|
+
|
166
|
+
extend Forwardable
|
167
|
+
def_delegators :'Faraday::Utils', :escape, :unescape
|
168
|
+
end
|
169
|
+
|
170
|
+
# Useful default for OAuth and caching.
|
171
|
+
@sort_params = true
|
172
|
+
|
173
|
+
extend EncodeMethods
|
174
|
+
extend DecodeMethods
|
175
|
+
end
|
176
|
+
end
|
data/lib/faraday/error.rb
CHANGED
@@ -1,23 +1,15 @@
|
|
1
|
-
|
2
|
-
class Error < StandardError; end
|
3
|
-
class MissingDependency < Error; end
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
3
|
+
# Faraday namespace.
|
4
|
+
module Faraday
|
5
|
+
# Faraday error base class.
|
6
|
+
class Error < StandardError
|
6
7
|
attr_reader :response, :wrapped_exception
|
7
8
|
|
8
|
-
def initialize(
|
9
|
-
@wrapped_exception = nil
|
10
|
-
@response = response
|
11
|
-
|
12
|
-
if ex.respond_to?(:backtrace)
|
13
|
-
super(ex.message)
|
14
|
-
@wrapped_exception = ex
|
15
|
-
elsif ex.respond_to?(:each_key)
|
16
|
-
super("the server responded with status #{ex[:status]}")
|
17
|
-
@response = ex
|
18
|
-
else
|
19
|
-
super(ex.to_s)
|
20
|
-
end
|
9
|
+
def initialize(exc = nil, response = nil)
|
10
|
+
@wrapped_exception = nil unless defined?(@wrapped_exception)
|
11
|
+
@response = nil unless defined?(@response)
|
12
|
+
super(exc_msg_and_response!(exc, response))
|
21
13
|
end
|
22
14
|
|
23
15
|
def backtrace
|
@@ -29,35 +21,127 @@ module Faraday
|
|
29
21
|
end
|
30
22
|
|
31
23
|
def inspect
|
32
|
-
inner = ''
|
33
|
-
if @wrapped_exception
|
34
|
-
|
35
|
-
|
36
|
-
if @response
|
37
|
-
inner << " response=#{@response.inspect}"
|
38
|
-
end
|
39
|
-
if inner.empty?
|
40
|
-
inner << " #{super}"
|
41
|
-
end
|
24
|
+
inner = +''
|
25
|
+
inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
|
26
|
+
inner << " response=#{@response.inspect}" if @response
|
27
|
+
inner << " #{super}" if inner.empty?
|
42
28
|
%(#<#{self.class}#{inner}>)
|
43
29
|
end
|
30
|
+
|
31
|
+
def response_status
|
32
|
+
@response[:status] if @response
|
33
|
+
end
|
34
|
+
|
35
|
+
def response_headers
|
36
|
+
@response[:headers] if @response
|
37
|
+
end
|
38
|
+
|
39
|
+
def response_body
|
40
|
+
@response[:body] if @response
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
# Pulls out potential parent exception and response hash, storing them in
|
46
|
+
# instance variables.
|
47
|
+
# exc - Either an Exception, a string message, or a response hash.
|
48
|
+
# response - Hash
|
49
|
+
# :status - Optional integer HTTP response status
|
50
|
+
# :headers - String key/value hash of HTTP response header
|
51
|
+
# values.
|
52
|
+
# :body - Optional string HTTP response body.
|
53
|
+
# :request - Hash
|
54
|
+
# :method - Symbol with the request HTTP method.
|
55
|
+
# :url - URI object with the url requested.
|
56
|
+
# :url_path - String with the url path requested.
|
57
|
+
# :params - String key/value hash of query params
|
58
|
+
# present in the request.
|
59
|
+
# :headers - String key/value hash of HTTP request
|
60
|
+
# header values.
|
61
|
+
# :body - String HTTP request body.
|
62
|
+
#
|
63
|
+
# If a subclass has to call this, then it should pass a string message
|
64
|
+
# to `super`. See NilStatusError.
|
65
|
+
def exc_msg_and_response!(exc, response = nil)
|
66
|
+
if @response.nil? && @wrapped_exception.nil?
|
67
|
+
@wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
|
68
|
+
return msg
|
69
|
+
end
|
70
|
+
|
71
|
+
exc.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
# Pulls out potential parent exception and response hash.
|
75
|
+
def exc_msg_and_response(exc, response = nil)
|
76
|
+
return [exc, exc.message, response] if exc.respond_to?(:backtrace)
|
77
|
+
|
78
|
+
return [nil, "the server responded with status #{exc[:status]}", exc] \
|
79
|
+
if exc.respond_to?(:each_key)
|
80
|
+
|
81
|
+
[nil, exc.to_s, response]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Faraday client error class. Represents 4xx status responses.
|
86
|
+
class ClientError < Error
|
87
|
+
end
|
88
|
+
|
89
|
+
# Raised by Faraday::Response::RaiseError in case of a 400 response.
|
90
|
+
class BadRequestError < ClientError
|
91
|
+
end
|
92
|
+
|
93
|
+
# Raised by Faraday::Response::RaiseError in case of a 401 response.
|
94
|
+
class UnauthorizedError < ClientError
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raised by Faraday::Response::RaiseError in case of a 403 response.
|
98
|
+
class ForbiddenError < ClientError
|
99
|
+
end
|
100
|
+
|
101
|
+
# Raised by Faraday::Response::RaiseError in case of a 404 response.
|
102
|
+
class ResourceNotFound < ClientError
|
44
103
|
end
|
45
104
|
|
46
|
-
|
47
|
-
class
|
48
|
-
|
105
|
+
# Raised by Faraday::Response::RaiseError in case of a 407 response.
|
106
|
+
class ProxyAuthError < ClientError
|
107
|
+
end
|
108
|
+
|
109
|
+
# Raised by Faraday::Response::RaiseError in case of a 409 response.
|
110
|
+
class ConflictError < ClientError
|
111
|
+
end
|
112
|
+
|
113
|
+
# Raised by Faraday::Response::RaiseError in case of a 422 response.
|
114
|
+
class UnprocessableEntityError < ClientError
|
115
|
+
end
|
116
|
+
|
117
|
+
# Faraday server error class. Represents 5xx status responses.
|
118
|
+
class ServerError < Error
|
119
|
+
end
|
120
|
+
|
121
|
+
# A unified client error for timeouts.
|
122
|
+
class TimeoutError < ServerError
|
123
|
+
def initialize(exc = 'timeout', response = nil)
|
124
|
+
super(exc, response)
|
125
|
+
end
|
126
|
+
end
|
49
127
|
|
50
|
-
|
51
|
-
|
52
|
-
|
128
|
+
# Raised by Faraday::Response::RaiseError in case of a nil status in response.
|
129
|
+
class NilStatusError < ServerError
|
130
|
+
def initialize(exc, response = nil)
|
131
|
+
exc_msg_and_response!(exc, response)
|
132
|
+
super('http status could not be derived from the server response')
|
53
133
|
end
|
54
134
|
end
|
55
135
|
|
56
|
-
|
136
|
+
# A unified error for failed connections.
|
137
|
+
class ConnectionFailed < Error
|
138
|
+
end
|
139
|
+
|
140
|
+
# A unified client error for SSL errors.
|
141
|
+
class SSLError < Error
|
57
142
|
end
|
58
143
|
|
59
|
-
|
60
|
-
|
61
|
-
Error.const_set(const, Faraday.const_get(const))
|
144
|
+
# Raised by middlewares that parse the response, like the JSON response middleware.
|
145
|
+
class ParsingError < Error
|
62
146
|
end
|
63
147
|
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
module Faraday
|
6
|
+
module Logging
|
7
|
+
# Serves as an integration point to customize logging
|
8
|
+
class Formatter
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
DEFAULT_OPTIONS = { headers: true, bodies: false,
|
12
|
+
log_level: :info }.freeze
|
13
|
+
|
14
|
+
def initialize(logger:, options:)
|
15
|
+
@logger = logger
|
16
|
+
@filter = []
|
17
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
21
|
+
|
22
|
+
def request(env)
|
23
|
+
request_log = proc do
|
24
|
+
"#{env.method.upcase} #{apply_filters(env.url.to_s)}"
|
25
|
+
end
|
26
|
+
public_send(log_level, 'request', &request_log)
|
27
|
+
|
28
|
+
log_headers('request', env.request_headers) if log_headers?(:request)
|
29
|
+
log_body('request', env[:body]) if env[:body] && log_body?(:request)
|
30
|
+
end
|
31
|
+
|
32
|
+
def response(env)
|
33
|
+
status = proc { "Status #{env.status}" }
|
34
|
+
public_send(log_level, 'response', &status)
|
35
|
+
|
36
|
+
log_headers('response', env.response_headers) if log_headers?(:response)
|
37
|
+
log_body('response', env[:body]) if env[:body] && log_body?(:response)
|
38
|
+
end
|
39
|
+
|
40
|
+
def filter(filter_word, filter_replacement)
|
41
|
+
@filter.push([filter_word, filter_replacement])
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def dump_headers(headers)
|
47
|
+
headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def dump_body(body)
|
51
|
+
if body.respond_to?(:to_str)
|
52
|
+
body.to_str
|
53
|
+
else
|
54
|
+
pretty_inspect(body)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def pretty_inspect(body)
|
59
|
+
body.pretty_inspect
|
60
|
+
end
|
61
|
+
|
62
|
+
def log_headers?(type)
|
63
|
+
case @options[:headers]
|
64
|
+
when Hash
|
65
|
+
@options[:headers][type]
|
66
|
+
else
|
67
|
+
@options[:headers]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def log_body?(type)
|
72
|
+
case @options[:bodies]
|
73
|
+
when Hash
|
74
|
+
@options[:bodies][type]
|
75
|
+
else
|
76
|
+
@options[:bodies]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def apply_filters(output)
|
81
|
+
@filter.each do |pattern, replacement|
|
82
|
+
output = output.to_s.gsub(pattern, replacement)
|
83
|
+
end
|
84
|
+
output
|
85
|
+
end
|
86
|
+
|
87
|
+
def log_level
|
88
|
+
unless %i[debug info warn error fatal].include?(@options[:log_level])
|
89
|
+
return :info
|
90
|
+
end
|
91
|
+
|
92
|
+
@options[:log_level]
|
93
|
+
end
|
94
|
+
|
95
|
+
def log_headers(type, headers)
|
96
|
+
headers_log = proc { apply_filters(dump_headers(headers)) }
|
97
|
+
public_send(log_level, type, &headers_log)
|
98
|
+
end
|
99
|
+
|
100
|
+
def log_body(type, body)
|
101
|
+
body_log = proc { apply_filters(dump_body(body)) }
|
102
|
+
public_send(log_level, type, &body_log)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/faraday/middleware.rb
CHANGED
@@ -1,37 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Faraday
|
4
|
+
# Middleware is the basic base class of any Faraday middleware.
|
2
5
|
class Middleware
|
3
6
|
extend MiddlewareRegistry
|
4
7
|
|
5
|
-
|
6
|
-
attr_accessor :load_error
|
7
|
-
private :load_error=
|
8
|
-
end
|
9
|
-
|
10
|
-
self.load_error = nil
|
11
|
-
|
12
|
-
# Executes a block which should try to require and reference dependent libraries
|
13
|
-
def self.dependency(lib = nil)
|
14
|
-
lib ? require(lib) : yield
|
15
|
-
rescue LoadError, NameError => error
|
16
|
-
self.load_error = error
|
17
|
-
end
|
8
|
+
attr_reader :app, :options
|
18
9
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.loaded?
|
25
|
-
load_error.nil?
|
10
|
+
def initialize(app = nil, options = {})
|
11
|
+
@app = app
|
12
|
+
@options = options
|
26
13
|
end
|
27
14
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
15
|
+
def call(env)
|
16
|
+
on_request(env) if respond_to?(:on_request)
|
17
|
+
app.call(env).on_complete do |environment|
|
18
|
+
on_complete(environment) if respond_to?(:on_complete)
|
19
|
+
end
|
31
20
|
end
|
32
21
|
|
33
|
-
def
|
34
|
-
|
22
|
+
def close
|
23
|
+
if app.respond_to?(:close)
|
24
|
+
app.close
|
25
|
+
else
|
26
|
+
warn "#{app} does not implement \#close!"
|
27
|
+
end
|
35
28
|
end
|
36
29
|
end
|
37
30
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'monitor'
|
4
|
+
|
5
|
+
module Faraday
|
6
|
+
# Adds the ability for other modules to register and lookup
|
7
|
+
# middleware classes.
|
8
|
+
module MiddlewareRegistry
|
9
|
+
def registered_middleware
|
10
|
+
@registered_middleware ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Register middleware class(es) on the current module.
|
14
|
+
#
|
15
|
+
# @param mappings [Hash] Middleware mappings from a lookup symbol to a middleware class.
|
16
|
+
# @return [void]
|
17
|
+
#
|
18
|
+
# @example Lookup by a constant
|
19
|
+
#
|
20
|
+
# module Faraday
|
21
|
+
# class Whatever < Middleware
|
22
|
+
# # Middleware looked up by :foo returns Faraday::Whatever::Foo.
|
23
|
+
# register_middleware(foo: Whatever)
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
def register_middleware(**mappings)
|
27
|
+
middleware_mutex do
|
28
|
+
registered_middleware.update(mappings)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Unregister a previously registered middleware class.
|
33
|
+
#
|
34
|
+
# @param key [Symbol] key for the registered middleware.
|
35
|
+
def unregister_middleware(key)
|
36
|
+
registered_middleware.delete(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Lookup middleware class with a registered Symbol shortcut.
|
40
|
+
#
|
41
|
+
# @param key [Symbol] key for the registered middleware.
|
42
|
+
# @return [Class] a middleware Class.
|
43
|
+
# @raise [Faraday::Error] if given key is not registered
|
44
|
+
#
|
45
|
+
# @example
|
46
|
+
#
|
47
|
+
# module Faraday
|
48
|
+
# class Whatever < Middleware
|
49
|
+
# register_middleware(foo: Whatever)
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# Faraday::Middleware.lookup_middleware(:foo)
|
54
|
+
# # => Faraday::Whatever
|
55
|
+
def lookup_middleware(key)
|
56
|
+
registered_middleware[key] ||
|
57
|
+
raise(Faraday::Error, "#{key.inspect} is not registered on #{self}")
|
58
|
+
end
|
59
|
+
|
60
|
+
def middleware_mutex(&block)
|
61
|
+
@middleware_mutex ||= Monitor.new
|
62
|
+
@middleware_mutex.synchronize(&block)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|