faraday 0.17.3 → 1.0.0.pre.rc1

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +1 -1
  3. data/README.md +18 -358
  4. data/lib/faraday.rb +93 -174
  5. data/lib/faraday/adapter.rb +36 -22
  6. data/lib/faraday/adapter/em_http.rb +142 -99
  7. data/lib/faraday/adapter/em_http_ssl_patch.rb +23 -17
  8. data/lib/faraday/adapter/em_synchrony.rb +104 -60
  9. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +18 -15
  10. data/lib/faraday/adapter/excon.rb +97 -57
  11. data/lib/faraday/adapter/httpclient.rb +61 -39
  12. data/lib/faraday/adapter/net_http.rb +104 -51
  13. data/lib/faraday/adapter/net_http_persistent.rb +49 -28
  14. data/lib/faraday/adapter/patron.rb +54 -35
  15. data/lib/faraday/adapter/rack.rb +28 -12
  16. data/lib/faraday/adapter/test.rb +86 -53
  17. data/lib/faraday/adapter/typhoeus.rb +4 -1
  18. data/lib/faraday/adapter_registry.rb +28 -0
  19. data/lib/faraday/autoload.rb +47 -36
  20. data/lib/faraday/connection.rb +321 -179
  21. data/lib/faraday/dependency_loader.rb +37 -0
  22. data/lib/faraday/encoders/flat_params_encoder.rb +94 -0
  23. data/lib/faraday/encoders/nested_params_encoder.rb +171 -0
  24. data/lib/faraday/error.rb +21 -79
  25. data/lib/faraday/logging/formatter.rb +92 -0
  26. data/lib/faraday/middleware.rb +4 -28
  27. data/lib/faraday/middleware_registry.rb +129 -0
  28. data/lib/faraday/options.rb +33 -184
  29. data/lib/faraday/options/connection_options.rb +22 -0
  30. data/lib/faraday/options/env.rb +181 -0
  31. data/lib/faraday/options/proxy_options.rb +28 -0
  32. data/lib/faraday/options/request_options.rb +21 -0
  33. data/lib/faraday/options/ssl_options.rb +59 -0
  34. data/lib/faraday/parameters.rb +4 -197
  35. data/lib/faraday/rack_builder.rb +66 -55
  36. data/lib/faraday/request.rb +68 -36
  37. data/lib/faraday/request/authorization.rb +42 -30
  38. data/lib/faraday/request/basic_authentication.rb +14 -7
  39. data/lib/faraday/request/instrumentation.rb +45 -27
  40. data/lib/faraday/request/multipart.rb +72 -49
  41. data/lib/faraday/request/retry.rb +197 -171
  42. data/lib/faraday/request/token_authentication.rb +15 -10
  43. data/lib/faraday/request/url_encoded.rb +41 -23
  44. data/lib/faraday/response.rb +22 -15
  45. data/lib/faraday/response/logger.rb +22 -69
  46. data/lib/faraday/response/raise_error.rb +36 -18
  47. data/lib/faraday/upload_io.rb +29 -18
  48. data/lib/faraday/utils.rb +28 -245
  49. data/lib/faraday/utils/headers.rb +139 -0
  50. data/lib/faraday/utils/params_hash.rb +61 -0
  51. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  52. metadata +25 -51
  53. data/CHANGELOG.md +0 -232
  54. data/Rakefile +0 -13
  55. data/lib/faraday/deprecate.rb +0 -107
  56. data/spec/faraday/deprecate_spec.rb +0 -69
  57. data/spec/faraday/error_spec.rb +0 -102
  58. data/spec/faraday/response/raise_error_spec.rb +0 -106
  59. data/spec/spec_helper.rb +0 -105
  60. data/test/adapters/default_test.rb +0 -14
  61. data/test/adapters/em_http_test.rb +0 -30
  62. data/test/adapters/em_synchrony_test.rb +0 -32
  63. data/test/adapters/excon_test.rb +0 -30
  64. data/test/adapters/httpclient_test.rb +0 -34
  65. data/test/adapters/integration.rb +0 -263
  66. data/test/adapters/logger_test.rb +0 -136
  67. data/test/adapters/net_http_persistent_test.rb +0 -114
  68. data/test/adapters/net_http_test.rb +0 -79
  69. data/test/adapters/patron_test.rb +0 -40
  70. data/test/adapters/rack_test.rb +0 -38
  71. data/test/adapters/test_middleware_test.rb +0 -157
  72. data/test/adapters/typhoeus_test.rb +0 -38
  73. data/test/authentication_middleware_test.rb +0 -65
  74. data/test/composite_read_io_test.rb +0 -109
  75. data/test/connection_test.rb +0 -738
  76. data/test/env_test.rb +0 -268
  77. data/test/helper.rb +0 -75
  78. data/test/live_server.rb +0 -67
  79. data/test/middleware/instrumentation_test.rb +0 -88
  80. data/test/middleware/retry_test.rb +0 -282
  81. data/test/middleware_stack_test.rb +0 -260
  82. data/test/multibyte.txt +0 -1
  83. data/test/options_test.rb +0 -333
  84. data/test/parameters_test.rb +0 -157
  85. data/test/request_middleware_test.rb +0 -126
  86. data/test/response_middleware_test.rb +0 -72
  87. data/test/strawberry.rb +0 -2
  88. data/test/utils_test.rb +0 -98
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ # DependencyLoader helps Faraday adapters and middleware load dependencies.
5
+ module DependencyLoader
6
+ attr_reader :load_error
7
+
8
+ # Executes a block which should try to require and reference dependent
9
+ # libraries
10
+ def dependency(lib = nil)
11
+ lib ? require(lib) : yield
12
+ rescue LoadError, NameError => e
13
+ self.load_error = e
14
+ end
15
+
16
+ def new(*)
17
+ unless loaded?
18
+ raise "missing dependency for #{self}: #{load_error.message}"
19
+ end
20
+
21
+ super
22
+ end
23
+
24
+ def loaded?
25
+ load_error.nil?
26
+ end
27
+
28
+ def inherited(subclass)
29
+ super
30
+ subclass.send(:load_error=, load_error)
31
+ end
32
+
33
+ private
34
+
35
+ attr_writer :load_error
36
+ end
37
+ end
@@ -0,0 +1,94 @@
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
+ # Useful default for OAuth and caching.
37
+ # Only to be used for non-Array inputs. Arrays should preserve order.
38
+ params.sort!
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
+ value.each do |sub_value|
49
+ encoded_value = escape(sub_value)
50
+ buffer << "#{encoded_key}=#{encoded_value}&"
51
+ end
52
+ else
53
+ encoded_value = escape(value)
54
+ buffer << "#{encoded_key}=#{encoded_value}&"
55
+ end
56
+ end
57
+ buffer.chop
58
+ end
59
+
60
+ # Decode converts the given URI querystring into a hash.
61
+ #
62
+ # @param query [String] query arguments to parse.
63
+ #
64
+ # @example
65
+ #
66
+ # decode('a=one&a=two&a=three&b=true&c=C')
67
+ # # => {"a"=>["one", "two", "three"], "b"=>"true", "c"=>"C"}
68
+ #
69
+ # @return [Hash] parsed keys and value strings from the querystring.
70
+ def self.decode(query)
71
+ return nil if query.nil?
72
+
73
+ empty_accumulator = {}
74
+
75
+ split_query = (query.split('&').map do |pair|
76
+ pair.split('=', 2) if pair && !pair.empty?
77
+ end).compact
78
+ split_query.each_with_object(empty_accumulator.dup) do |pair, accu|
79
+ pair[0] = unescape(pair[0])
80
+ pair[1] = true if pair[1].nil?
81
+ if pair[1].respond_to?(:to_str)
82
+ pair[1] = unescape(pair[1].to_str.tr('+', ' '))
83
+ end
84
+ if accu[pair[0]].is_a?(Array)
85
+ accu[pair[0]] << pair[1]
86
+ elsif accu[pair[0]]
87
+ accu[pair[0]] = [accu[pair[0]], pair[1]]
88
+ else
89
+ accu[pair[0]] = pair[1]
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,171 @@
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
+ # Useful default for OAuth and caching.
25
+ # Only to be used for non-Array inputs. Arrays should preserve order.
26
+ params.sort!
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 = $` 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
+ extend Forwardable
165
+ def_delegators :'Faraday::Utils', :escape, :unescape
166
+ end
167
+
168
+ extend EncodeMethods
169
+ extend DecodeMethods
170
+ end
171
+ end
@@ -1,17 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'faraday/deprecate'
4
-
5
- # Faraday namespace.
6
3
  module Faraday
7
4
  # Faraday error base class.
8
5
  class Error < StandardError
9
6
  attr_reader :response, :wrapped_exception
10
7
 
11
8
  def initialize(exc, response = nil)
12
- @wrapped_exception = nil unless defined?(@wrapped_exception)
13
- @response = nil unless defined?(@response)
14
- super(exc_msg_and_response!(exc, response))
9
+ @wrapped_exception = nil
10
+ @response = response
11
+
12
+ if exc.respond_to?(:backtrace)
13
+ super(exc.message)
14
+ @wrapped_exception = exc
15
+ elsif exc.respond_to?(:each_key)
16
+ super("the server responded with status #{exc[:status]}")
17
+ @response = exc
18
+ else
19
+ super(exc.to_s)
20
+ end
15
21
  end
16
22
 
17
23
  def backtrace
@@ -23,44 +29,12 @@ module Faraday
23
29
  end
24
30
 
25
31
  def inspect
26
- inner = ''
27
- inner += " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
28
- inner += " response=#{@response.inspect}" if @response
29
- inner += " #{super}" if inner.empty?
32
+ inner = +''
33
+ inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
34
+ inner << " response=#{@response.inspect}" if @response
35
+ inner << " #{super}" if inner.empty?
30
36
  %(#<#{self.class}#{inner}>)
31
37
  end
32
-
33
- protected
34
-
35
- # Pulls out potential parent exception and response hash, storing them in
36
- # instance variables.
37
- # exc - Either an Exception, a string message, or a response hash.
38
- # response - Hash
39
- # :status - Optional integer HTTP response status
40
- # :headers - String key/value hash of HTTP response header
41
- # values.
42
- # :body - Optional string HTTP response body.
43
- #
44
- # If a subclass has to call this, then it should pass a string message
45
- # to `super`. See NilStatusError.
46
- def exc_msg_and_response!(exc, response = nil)
47
- if @response.nil? && @wrapped_exception.nil?
48
- @wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
49
- return msg
50
- end
51
-
52
- exc.to_s
53
- end
54
-
55
- # Pulls out potential parent exception and response hash.
56
- def exc_msg_and_response(exc, response = nil)
57
- return [exc, exc.message, response] if exc.respond_to?(:backtrace)
58
-
59
- return [nil, "the server responded with status #{exc[:status]}", exc] \
60
- if exc.respond_to?(:each_key)
61
-
62
- [nil, exc.to_s, response]
63
- end
64
38
  end
65
39
 
66
40
  # Faraday client error class. Represents 4xx status responses.
@@ -100,59 +74,27 @@ module Faraday
100
74
  end
101
75
 
102
76
  # A unified client error for timeouts.
103
- class TimeoutError < ClientError
77
+ class TimeoutError < ServerError
104
78
  def initialize(exc = 'timeout', response = nil)
105
79
  super(exc, response)
106
80
  end
107
81
  end
108
82
 
109
- # Raised by Faraday::Response::RaiseError in case of a nil status in response.
110
- class NilStatusError < ServerError
111
- def initialize(exc, response = nil)
112
- exc_msg_and_response!(exc, response)
113
- @response = unwrap_resp!(@response)
114
- super('http status could not be derived from the server response')
115
- end
116
-
117
- private
118
-
119
- extend Faraday::Deprecate
120
-
121
- def unwrap_resp(resp)
122
- if inner = (resp.keys.size == 1 && resp[:response])
123
- return unwrap_resp(inner)
124
- end
125
-
126
- resp
127
- end
128
-
129
- alias_method :unwrap_resp!, :unwrap_resp
130
- deprecate('unwrap_resp', nil, '1.0')
131
- end
132
-
133
83
  # A unified error for failed connections.
134
- class ConnectionFailed < ClientError
84
+ class ConnectionFailed < Error
135
85
  end
136
86
 
137
87
  # A unified client error for SSL errors.
138
- class SSLError < ClientError
88
+ class SSLError < Error
139
89
  end
140
90
 
141
91
  # Raised by FaradayMiddleware::ResponseMiddleware
142
- class ParsingError < ClientError
92
+ class ParsingError < Error
143
93
  end
144
94
 
145
95
  # Exception used to control the Retry middleware.
146
96
  #
147
97
  # @see Faraday::Request::Retry
148
- class RetriableResponse < ClientError
149
- end
150
-
151
- [:ClientError, :ConnectionFailed, :ResourceNotFound,
152
- :ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const|
153
- Error.const_set(
154
- const,
155
- DeprecatedClass.proxy_class(Faraday.const_get(const))
156
- )
98
+ class RetriableResponse < Error
157
99
  end
158
100
  end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pp'
4
+ module Faraday
5
+ module Logging
6
+ # Serves as an integration point to customize logging
7
+ class Formatter
8
+ extend Forwardable
9
+
10
+ DEFAULT_OPTIONS = { headers: true, bodies: false }.freeze
11
+
12
+ def initialize(logger:, options:)
13
+ @logger = logger
14
+ @filter = []
15
+ @options = DEFAULT_OPTIONS.merge(options)
16
+ end
17
+
18
+ def_delegators :@logger, :debug, :info, :warn, :error, :fatal
19
+
20
+ def request(env)
21
+ info('request') do
22
+ "#{env.method.upcase} #{apply_filters(env.url.to_s)}"
23
+ end
24
+ if log_headers?(:request)
25
+ debug('request') { apply_filters(dump_headers(env.request_headers)) }
26
+ end
27
+ return unless env[:body] && log_body?(:request)
28
+
29
+ debug('request') { apply_filters(dump_body(env[:body])) }
30
+ end
31
+
32
+ def response(env)
33
+ info('response') { "Status #{env.status}" }
34
+ if log_headers?(:response)
35
+ debug('response') do
36
+ apply_filters(dump_headers(env.response_headers))
37
+ end
38
+ end
39
+ return unless env[:body] && log_body?(:response)
40
+
41
+ debug('response') { apply_filters(dump_body(env[:body])) }
42
+ end
43
+
44
+ def filter(filter_word, filter_replacement)
45
+ @filter.push([filter_word, filter_replacement])
46
+ end
47
+
48
+ private
49
+
50
+ def dump_headers(headers)
51
+ headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
52
+ end
53
+
54
+ def dump_body(body)
55
+ if body.respond_to?(:to_str)
56
+ body.to_str
57
+ else
58
+ pretty_inspect(body)
59
+ end
60
+ end
61
+
62
+ def pretty_inspect(body)
63
+ body.pretty_inspect
64
+ end
65
+
66
+ def log_headers?(type)
67
+ case @options[:headers]
68
+ when Hash
69
+ @options[:headers][type]
70
+ else
71
+ @options[:headers]
72
+ end
73
+ end
74
+
75
+ def log_body?(type)
76
+ case @options[:bodies]
77
+ when Hash
78
+ @options[:bodies][type]
79
+ else
80
+ @options[:bodies]
81
+ end
82
+ end
83
+
84
+ def apply_filters(output)
85
+ @filter.each do |pattern, replacement|
86
+ output = output.to_s.gsub(pattern, replacement)
87
+ end
88
+ output
89
+ end
90
+ end
91
+ end
92
+ end