faraday 0.12.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +350 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +22 -325
  5. data/Rakefile +7 -0
  6. data/examples/client_spec.rb +65 -0
  7. data/examples/client_test.rb +79 -0
  8. data/lib/faraday.rb +120 -188
  9. data/lib/faraday/adapter.rb +84 -22
  10. data/lib/faraday/adapter/em_http.rb +150 -104
  11. data/lib/faraday/adapter/em_http_ssl_patch.rb +24 -18
  12. data/lib/faraday/adapter/em_synchrony.rb +110 -63
  13. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +18 -15
  14. data/lib/faraday/adapter/excon.rb +98 -54
  15. data/lib/faraday/adapter/httpclient.rb +83 -59
  16. data/lib/faraday/adapter/net_http_persistent.rb +68 -27
  17. data/lib/faraday/adapter/patron.rb +86 -37
  18. data/lib/faraday/adapter/rack.rb +30 -13
  19. data/lib/faraday/adapter/test.rb +103 -62
  20. data/lib/faraday/adapter/typhoeus.rb +7 -115
  21. data/lib/faraday/adapter_registry.rb +30 -0
  22. data/lib/faraday/autoload.rb +46 -36
  23. data/lib/faraday/connection.rb +336 -177
  24. data/lib/faraday/dependency_loader.rb +37 -0
  25. data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
  26. data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
  27. data/lib/faraday/error.rb +126 -37
  28. data/lib/faraday/file_part.rb +128 -0
  29. data/lib/faraday/logging/formatter.rb +105 -0
  30. data/lib/faraday/methods.rb +6 -0
  31. data/lib/faraday/middleware.rb +19 -25
  32. data/lib/faraday/middleware_registry.rb +129 -0
  33. data/lib/faraday/options.rb +39 -193
  34. data/lib/faraday/options/connection_options.rb +22 -0
  35. data/lib/faraday/options/env.rb +181 -0
  36. data/lib/faraday/options/proxy_options.rb +28 -0
  37. data/lib/faraday/options/request_options.rb +22 -0
  38. data/lib/faraday/options/ssl_options.rb +59 -0
  39. data/lib/faraday/param_part.rb +53 -0
  40. data/lib/faraday/parameters.rb +4 -196
  41. data/lib/faraday/rack_builder.rb +77 -63
  42. data/lib/faraday/request.rb +94 -32
  43. data/lib/faraday/request/authorization.rb +44 -30
  44. data/lib/faraday/request/basic_authentication.rb +14 -7
  45. data/lib/faraday/request/instrumentation.rb +45 -27
  46. data/lib/faraday/request/multipart.rb +86 -48
  47. data/lib/faraday/request/retry.rb +209 -134
  48. data/lib/faraday/request/token_authentication.rb +15 -10
  49. data/lib/faraday/request/url_encoded.rb +43 -23
  50. data/lib/faraday/response.rb +27 -23
  51. data/lib/faraday/response/logger.rb +22 -69
  52. data/lib/faraday/response/raise_error.rb +49 -14
  53. data/lib/faraday/utils.rb +38 -247
  54. data/lib/faraday/utils/headers.rb +139 -0
  55. data/lib/faraday/utils/params_hash.rb +61 -0
  56. data/lib/faraday/version.rb +5 -0
  57. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  58. data/spec/faraday/adapter/em_http_spec.rb +47 -0
  59. data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
  60. data/spec/faraday/adapter/excon_spec.rb +49 -0
  61. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  62. data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
  63. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  64. data/spec/faraday/adapter/patron_spec.rb +18 -0
  65. data/spec/faraday/adapter/rack_spec.rb +8 -0
  66. data/spec/faraday/adapter/test_spec.rb +260 -0
  67. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  68. data/spec/faraday/adapter_registry_spec.rb +28 -0
  69. data/spec/faraday/adapter_spec.rb +55 -0
  70. data/spec/faraday/composite_read_io_spec.rb +80 -0
  71. data/spec/faraday/connection_spec.rb +691 -0
  72. data/spec/faraday/error_spec.rb +60 -0
  73. data/spec/faraday/middleware_spec.rb +52 -0
  74. data/spec/faraday/options/env_spec.rb +70 -0
  75. data/spec/faraday/options/options_spec.rb +297 -0
  76. data/spec/faraday/options/proxy_options_spec.rb +37 -0
  77. data/spec/faraday/options/request_options_spec.rb +19 -0
  78. data/spec/faraday/params_encoders/flat_spec.rb +42 -0
  79. data/spec/faraday/params_encoders/nested_spec.rb +142 -0
  80. data/spec/faraday/rack_builder_spec.rb +345 -0
  81. data/spec/faraday/request/authorization_spec.rb +88 -0
  82. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  83. data/spec/faraday/request/multipart_spec.rb +302 -0
  84. data/spec/faraday/request/retry_spec.rb +242 -0
  85. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  86. data/spec/faraday/request_spec.rb +120 -0
  87. data/spec/faraday/response/logger_spec.rb +220 -0
  88. data/spec/faraday/response/middleware_spec.rb +68 -0
  89. data/spec/faraday/response/raise_error_spec.rb +169 -0
  90. data/spec/faraday/response_spec.rb +75 -0
  91. data/spec/faraday/utils/headers_spec.rb +82 -0
  92. data/spec/faraday/utils_spec.rb +56 -0
  93. data/spec/faraday_spec.rb +37 -0
  94. data/spec/spec_helper.rb +132 -0
  95. data/spec/support/disabling_stub.rb +14 -0
  96. data/spec/support/fake_safe_buffer.rb +15 -0
  97. data/spec/support/helper_methods.rb +133 -0
  98. data/spec/support/shared_examples/adapter.rb +105 -0
  99. data/spec/support/shared_examples/params_encoder.rb +18 -0
  100. data/spec/support/shared_examples/request_method.rb +262 -0
  101. data/spec/support/streaming_response_checker.rb +35 -0
  102. data/spec/support/webmock_rack_app.rb +68 -0
  103. metadata +109 -10
  104. data/lib/faraday/adapter/net_http.rb +0 -135
  105. data/lib/faraday/upload_io.rb +0 -67
@@ -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,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 = $` 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
- module Faraday
2
- class Error < StandardError; end
3
- class MissingDependency < Error; end
1
+ # frozen_string_literal: true
4
2
 
5
- class ClientError < Error
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(ex, response = nil)
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, 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,132 @@ module Faraday
29
21
  end
30
22
 
31
23
  def inspect
32
- inner = ''
33
- if @wrapped_exception
34
- inner << " wrapped=#{@wrapped_exception.inspect}"
35
- end
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_path - String with the url path requested.
56
+ # :params - String key/value hash of query params
57
+ # present in the request.
58
+ # :headers - String key/value hash of HTTP request
59
+ # header values.
60
+ # :body - String HTTP request body.
61
+ #
62
+ # If a subclass has to call this, then it should pass a string message
63
+ # to `super`. See NilStatusError.
64
+ def exc_msg_and_response!(exc, response = nil)
65
+ if @response.nil? && @wrapped_exception.nil?
66
+ @wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
67
+ return msg
68
+ end
69
+
70
+ exc.to_s
71
+ end
72
+
73
+ # Pulls out potential parent exception and response hash.
74
+ def exc_msg_and_response(exc, response = nil)
75
+ return [exc, exc.message, response] if exc.respond_to?(:backtrace)
76
+
77
+ return [nil, "the server responded with status #{exc[:status]}", exc] \
78
+ if exc.respond_to?(:each_key)
79
+
80
+ [nil, exc.to_s, response]
81
+ end
44
82
  end
45
83
 
46
- class ConnectionFailed < ClientError; end
47
- class ResourceNotFound < ClientError; end
48
- class ParsingError < ClientError; end
84
+ # Faraday client error class. Represents 4xx status responses.
85
+ class ClientError < Error
86
+ end
87
+
88
+ # Raised by Faraday::Response::RaiseError in case of a 400 response.
89
+ class BadRequestError < ClientError
90
+ end
49
91
 
50
- class TimeoutError < ClientError
51
- def initialize(ex = nil)
52
- super(ex || "timeout")
92
+ # Raised by Faraday::Response::RaiseError in case of a 401 response.
93
+ class UnauthorizedError < ClientError
94
+ end
95
+
96
+ # Raised by Faraday::Response::RaiseError in case of a 403 response.
97
+ class ForbiddenError < ClientError
98
+ end
99
+
100
+ # Raised by Faraday::Response::RaiseError in case of a 404 response.
101
+ class ResourceNotFound < ClientError
102
+ end
103
+
104
+ # Raised by Faraday::Response::RaiseError in case of a 407 response.
105
+ class ProxyAuthError < ClientError
106
+ end
107
+
108
+ # Raised by Faraday::Response::RaiseError in case of a 409 response.
109
+ class ConflictError < ClientError
110
+ end
111
+
112
+ # Raised by Faraday::Response::RaiseError in case of a 422 response.
113
+ class UnprocessableEntityError < ClientError
114
+ end
115
+
116
+ # Faraday server error class. Represents 5xx status responses.
117
+ class ServerError < Error
118
+ end
119
+
120
+ # A unified client error for timeouts.
121
+ class TimeoutError < ServerError
122
+ def initialize(exc = 'timeout', response = nil)
123
+ super(exc, response)
124
+ end
125
+ end
126
+
127
+ # Raised by Faraday::Response::RaiseError in case of a nil status in response.
128
+ class NilStatusError < ServerError
129
+ def initialize(exc, response = nil)
130
+ exc_msg_and_response!(exc, response)
131
+ super('http status could not be derived from the server response')
53
132
  end
54
133
  end
55
134
 
56
- class SSLError < ClientError
135
+ # A unified error for failed connections.
136
+ class ConnectionFailed < Error
137
+ end
138
+
139
+ # A unified client error for SSL errors.
140
+ class SSLError < Error
141
+ end
142
+
143
+ # Raised by FaradayMiddleware::ResponseMiddleware
144
+ class ParsingError < Error
57
145
  end
58
146
 
59
- [:MissingDependency, :ClientError, :ConnectionFailed, :ResourceNotFound,
60
- :ParsingError, :TimeoutError, :SSLError].each do |const|
61
- Error.const_set(const, Faraday.const_get(const))
147
+ # Exception used to control the Retry middleware.
148
+ #
149
+ # @see Faraday::Request::Retry
150
+ class RetriableResponse < Error
62
151
  end
63
152
  end