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
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
3
5
  # Sends requests to a Rack app.
4
6
  #
5
- # Examples
7
+ # @example
6
8
  #
7
9
  # class MyRackApp
8
10
  # def call(env)
@@ -17,7 +19,7 @@ module Faraday
17
19
  dependency 'rack/test'
18
20
 
19
21
  # not prefixed with "HTTP_"
20
- SPECIAL_HEADERS = %w[ CONTENT_LENGTH CONTENT_TYPE ]
22
+ SPECIAL_HEADERS = %w[CONTENT_LENGTH CONTENT_TYPE].freeze
21
23
 
22
24
  def initialize(faraday_app, rack_app)
23
25
  super(faraday_app)
@@ -27,32 +29,47 @@ module Faraday
27
29
 
28
30
  def call(env)
29
31
  super
30
- rack_env = {
31
- :method => env[:method],
32
- :input => env[:body].respond_to?(:read) ? env[:body].read : env[:body],
33
- 'rack.url_scheme' => env[:url].scheme
34
- }
32
+ rack_env = build_rack_env(env)
35
33
 
36
- env[:request_headers].each do |name, value|
34
+ env[:request_headers]&.each do |name, value|
37
35
  name = name.upcase.tr('-', '_')
38
36
  name = "HTTP_#{name}" unless SPECIAL_HEADERS.include? name
39
37
  rack_env[name] = value
40
- end if env[:request_headers]
38
+ end
41
39
 
42
- timeout = env[:request][:timeout] || env[:request][:open_timeout]
40
+ timeout = request_timeout(:open, env[:request])
41
+ timeout ||= request_timeout(:read, env[:request])
43
42
  response = if timeout
44
- Timer.timeout(timeout, Faraday::Error::TimeoutError) { execute_request(env, rack_env) }
45
- else
46
- execute_request(env, rack_env)
43
+ Timer.timeout(timeout, Faraday::TimeoutError) do
44
+ execute_request(env, rack_env)
45
+ end
46
+ else
47
+ execute_request(env, rack_env)
48
+ end
49
+
50
+ if (req = env[:request]).stream_response?
51
+ warn "Streaming downloads for #{self.class.name} " \
52
+ 'are not yet implemented.'
53
+ req.on_data.call(response.body, response.body.bytesize)
47
54
  end
48
55
 
49
56
  save_response(env, response.status, response.body, response.headers)
50
57
  @app.call env
51
58
  end
52
59
 
60
+ private
61
+
53
62
  def execute_request(env, rack_env)
54
63
  @session.request(env[:url].to_s, rack_env)
55
64
  end
65
+
66
+ def build_rack_env(env)
67
+ {
68
+ method: env[:method],
69
+ input: env[:body].respond_to?(:read) ? env[:body].read : env[:body],
70
+ 'rack.url_scheme' => env[:url].scheme
71
+ }
72
+ end
56
73
  end
57
74
  end
58
75
  end
@@ -1,51 +1,56 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
3
- # Examples
4
- #
5
+ # @example
5
6
  # test = Faraday::Connection.new do
6
7
  # use Faraday::Adapter::Test do |stub|
7
- # # simply define matcher to match the request
8
+ # # Define matcher to match the request
8
9
  # stub.get '/resource.json' do
9
10
  # # return static content
10
11
  # [200, {'Content-Type' => 'application/json'}, 'hi world']
11
12
  # end
12
- #
13
+ #
13
14
  # # response with content generated based on request
14
15
  # stub.get '/showget' do |env|
15
16
  # [200, {'Content-Type' => 'text/plain'}, env[:method].to_s]
16
17
  # end
17
- #
18
- # # regular expression can be used as matching filter
18
+ #
19
+ # # A regular expression can be used as matching filter
19
20
  # stub.get /\A\/items\/(\d+)\z/ do |env, meta|
20
- # # in case regular expression is used an instance of MatchData can be received
21
- # [200, {'Content-Type' => 'text/plain'}, "showing item: #{meta[:match_data][1]}"]
21
+ # # in case regular expression is used, an instance of MatchData
22
+ # # can be received
23
+ # [200,
24
+ # {'Content-Type' => 'text/plain'},
25
+ # "showing item: #{meta[:match_data][1]}"
26
+ # ]
22
27
  # end
23
28
  # end
24
29
  # end
25
- #
30
+ #
26
31
  # resp = test.get '/resource.json'
27
32
  # resp.body # => 'hi world'
28
- #
33
+ #
29
34
  # resp = test.get '/showget'
30
35
  # resp.body # => 'get'
31
- #
36
+ #
32
37
  # resp = test.get '/items/1'
33
38
  # resp.body # => 'showing item: 1'
34
- #
39
+ #
35
40
  # resp = test.get '/items/2'
36
41
  # resp.body # => 'showing item: 2'
37
- #
38
-
39
42
  class Test < Faraday::Adapter
40
43
  attr_accessor :stubs
41
44
 
45
+ # A stack of Stubs
42
46
  class Stubs
43
47
  class NotFound < StandardError
44
48
  end
45
49
 
46
50
  def initialize
47
- # {:get => [Stub, Stub]}
48
- @stack, @consumed = {}, {}
51
+ # { get: [Stub, Stub] }
52
+ @stack = {}
53
+ @consumed = {}
49
54
  yield(self) if block_given?
50
55
  end
51
56
 
@@ -53,17 +58,18 @@ module Faraday
53
58
  @stack.empty?
54
59
  end
55
60
 
56
- def match(request_method, path, headers, body)
57
- return false if !@stack.key?(request_method)
61
+ def match(request_method, host, path, headers, body)
62
+ return false unless @stack.key?(request_method)
63
+
58
64
  stack = @stack[request_method]
59
65
  consumed = (@consumed[request_method] ||= [])
60
66
 
61
- stub, meta = matches?(stack, path, headers, body)
67
+ stub, meta = matches?(stack, host, path, headers, body)
62
68
  if stub
63
69
  consumed << stack.delete(stub)
64
70
  return stub, meta
65
71
  end
66
- matches?(consumed, path, headers, body)
72
+ matches?(consumed, host, path, headers, body)
67
73
  end
68
74
 
69
75
  def get(path, headers = {}, &block)
@@ -74,15 +80,15 @@ module Faraday
74
80
  new_stub(:head, path, headers, &block)
75
81
  end
76
82
 
77
- def post(path, body=nil, headers = {}, &block)
83
+ def post(path, body = nil, headers = {}, &block)
78
84
  new_stub(:post, path, headers, body, &block)
79
85
  end
80
86
 
81
- def put(path, body=nil, headers = {}, &block)
87
+ def put(path, body = nil, headers = {}, &block)
82
88
  new_stub(:put, path, headers, body, &block)
83
89
  end
84
90
 
85
- def patch(path, body=nil, headers = {}, &block)
91
+ def patch(path, body = nil, headers = {}, &block)
86
92
  new_stub(:patch, path, headers, body, &block)
87
93
  end
88
94
 
@@ -98,56 +104,79 @@ module Faraday
98
104
  def verify_stubbed_calls
99
105
  failed_stubs = []
100
106
  @stack.each do |method, stubs|
101
- unless stubs.size == 0
102
- failed_stubs.concat(stubs.map {|stub|
107
+ next if stubs.empty?
108
+
109
+ failed_stubs.concat(
110
+ stubs.map do |stub|
103
111
  "Expected #{method} #{stub}."
104
- })
105
- end
112
+ end
113
+ )
106
114
  end
107
- raise failed_stubs.join(" ") unless failed_stubs.size == 0
115
+ raise failed_stubs.join(' ') unless failed_stubs.empty?
108
116
  end
109
117
 
110
118
  protected
111
119
 
112
- def new_stub(request_method, path, headers = {}, body=nil, &block)
113
- normalized_path = path.is_a?(Regexp) ? path : Faraday::Utils.normalize_path(path)
114
- (@stack[request_method] ||= []) << Stub.new(normalized_path, headers, body, block)
120
+ def new_stub(request_method, path, headers = {}, body = nil, &block)
121
+ normalized_path, host =
122
+ if path.is_a?(Regexp)
123
+ path
124
+ else
125
+ [
126
+ Faraday::Utils.normalize_path(path),
127
+ Faraday::Utils.URI(path).host
128
+ ]
129
+ end
130
+
131
+ stub = Stub.new(host, normalized_path, headers, body, block)
132
+ (@stack[request_method] ||= []) << stub
115
133
  end
116
134
 
117
- def matches?(stack, path, headers, body)
135
+ def matches?(stack, host, path, headers, body)
118
136
  stack.each do |stub|
119
- match_result, meta = stub.matches?(path, headers, body)
137
+ match_result, meta = stub.matches?(host, path, headers, body)
120
138
  return stub, meta if match_result
121
139
  end
122
140
  nil
123
141
  end
124
142
  end
125
143
 
126
- class Stub < Struct.new(:path, :params, :headers, :body, :block)
127
- def initialize(full, headers, body, block)
128
- path, query = full.respond_to?(:split) ? full.split("?") : full
129
- params = query ?
130
- Faraday::Utils.parse_nested_query(query) :
131
- {}
132
- super(path, params, headers, body, block)
144
+ # Stub request
145
+ # rubocop:disable Style/StructInheritance
146
+ class Stub < Struct.new(:host, :path, :params, :headers, :body, :block)
147
+ # rubocop:enable Style/StructInheritance
148
+ def initialize(host, full, headers, body, block)
149
+ path, query = full.respond_to?(:split) ? full.split('?') : full
150
+ params =
151
+ if query
152
+ Faraday::Utils.parse_nested_query(query)
153
+ else
154
+ {}
155
+ end
156
+
157
+ super(host, path, params, headers, body, block)
133
158
  end
134
159
 
135
- def matches?(request_uri, request_headers, request_body)
160
+ def matches?(request_host, request_uri, request_headers, request_body)
136
161
  request_path, request_query = request_uri.split('?')
137
- request_params = request_query ?
138
- Faraday::Utils.parse_nested_query(request_query) :
139
- {}
140
- # meta is a hash use as carrier
162
+ request_params =
163
+ if request_query
164
+ Faraday::Utils.parse_nested_query(request_query)
165
+ else
166
+ {}
167
+ end
168
+ # meta is a hash used as carrier
141
169
  # that will be yielded to consumer block
142
170
  meta = {}
143
- return path_match?(request_path, meta) &&
171
+ [(host.nil? || host == request_host) &&
172
+ path_match?(request_path, meta) &&
144
173
  params_match?(request_params) &&
145
174
  (body.to_s.size.zero? || request_body == body) &&
146
- headers_match?(request_headers), meta
175
+ headers_match?(request_headers), meta]
147
176
  end
148
177
 
149
178
  def path_match?(request_path, meta)
150
- if path.is_a? Regexp
179
+ if path.is_a?(Regexp)
151
180
  !!(meta[:match_data] = path.match(request_path))
152
181
  else
153
182
  path == request_path
@@ -171,7 +200,7 @@ module Faraday
171
200
  end
172
201
  end
173
202
 
174
- def initialize(app, stubs=nil, &block)
203
+ def initialize(app, stubs = nil, &block)
175
204
  super(app)
176
205
  @stubs = stubs || Stubs.new
177
206
  configure(&block) if block
@@ -183,21 +212,33 @@ module Faraday
183
212
 
184
213
  def call(env)
185
214
  super
215
+ host = env[:url].host
186
216
  normalized_path = Faraday::Utils.normalize_path(env[:url])
187
- params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder
188
-
189
- stub, meta = stubs.match(env[:method], normalized_path, env.request_headers, env[:body])
190
- if stub
191
- env[:params] = (query = env[:url].query) ?
192
- params_encoder.decode(query) : {}
193
- block_arity = stub.block.arity
194
- status, headers, body = (block_arity >= 0) ?
195
- stub.block.call(*[env, meta].take(block_arity)) :
217
+ params_encoder = env.request.params_encoder ||
218
+ Faraday::Utils.default_params_encoder
219
+
220
+ stub, meta = stubs.match(env[:method], host, normalized_path,
221
+ env.request_headers, env[:body])
222
+
223
+ unless stub
224
+ raise Stubs::NotFound, "no stubbed request for #{env[:method]} "\
225
+ "#{normalized_path} #{env[:body]}"
226
+ end
227
+
228
+ env[:params] = if (query = env[:url].query)
229
+ params_encoder.decode(query)
230
+ else
231
+ {}
232
+ end
233
+ block_arity = stub.block.arity
234
+ status, headers, body =
235
+ if block_arity >= 0
236
+ stub.block.call(*[env, meta].take(block_arity))
237
+ else
196
238
  stub.block.call(env, meta)
197
- save_response(env, status, body, headers)
198
- else
199
- raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
200
- end
239
+ end
240
+ save_response(env, status, body, headers)
241
+
201
242
  @app.call(env)
202
243
  end
203
244
  end
@@ -1,123 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
5
+ # Typhoeus adapter. This class is just a stub, the real adapter is in
6
+ # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
3
7
  class Typhoeus < Faraday::Adapter
4
- self.supports_parallel = true
5
-
6
- def self.setup_parallel_manager(options = {})
7
- options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options)
8
- end
8
+ # Needs to define this method in order to support Typhoeus <= 1.3.0
9
+ def call; end
9
10
 
10
11
  dependency 'typhoeus'
11
-
12
- def call(env)
13
- super
14
- perform_request env
15
- @app.call env
16
- end
17
-
18
- def perform_request(env)
19
- read_body env
20
-
21
- hydra = env[:parallel_manager] || self.class.setup_parallel_manager
22
- hydra.queue request(env)
23
- hydra.run unless parallel?(env)
24
- rescue Errno::ECONNREFUSED
25
- raise Error::ConnectionFailed, $!
26
- end
27
-
28
- # TODO: support streaming requests
29
- def read_body(env)
30
- env[:body] = env[:body].read if env[:body].respond_to? :read
31
- end
32
-
33
- def request(env)
34
- method = env[:method]
35
- # For some reason, prevents Typhoeus from using "100-continue".
36
- # We want this because Webrick 1.3.1 can't seem to handle it w/ PUT.
37
- method = method.to_s.upcase if method == :put
38
-
39
- req = ::Typhoeus::Request.new env[:url].to_s,
40
- :method => method,
41
- :body => env[:body],
42
- :headers => env[:request_headers],
43
- :disable_ssl_peer_verification => (env[:ssl] && env[:ssl].disable?)
44
-
45
- configure_ssl req, env
46
- configure_proxy req, env
47
- configure_timeout req, env
48
- configure_socket req, env
49
-
50
- req.on_complete do |resp|
51
- if resp.timed_out?
52
- if parallel?(env)
53
- # TODO: error callback in async mode
54
- else
55
- raise Faraday::Error::TimeoutError, "request timed out"
56
- end
57
- end
58
-
59
- case resp.curl_return_code
60
- when 0
61
- # everything OK
62
- when 7
63
- raise Error::ConnectionFailed, resp.curl_error_message
64
- when 60
65
- raise Faraday::SSLError, resp.curl_error_message
66
- else
67
- raise Error::ClientError, resp.curl_error_message
68
- end
69
-
70
- save_response(env, resp.code, resp.body) do |response_headers|
71
- response_headers.parse resp.headers
72
- end
73
- # in async mode, :response is initialized at this point
74
- env[:response].finish(env) if parallel?(env)
75
- end
76
-
77
- req
78
- end
79
-
80
- def configure_ssl(req, env)
81
- ssl = env[:ssl]
82
-
83
- req.ssl_version = ssl[:version] if ssl[:version]
84
- req.ssl_cert = ssl[:client_cert] if ssl[:client_cert]
85
- req.ssl_key = ssl[:client_key] if ssl[:client_key]
86
- req.ssl_cacert = ssl[:ca_file] if ssl[:ca_file]
87
- req.ssl_capath = ssl[:ca_path] if ssl[:ca_path]
88
- end
89
-
90
- def configure_proxy(req, env)
91
- proxy = request_options(env)[:proxy]
92
- return unless proxy
93
-
94
- req.proxy = "#{proxy[:uri].host}:#{proxy[:uri].port}"
95
-
96
- if proxy[:user] && proxy[:password]
97
- req.proxy_username = proxy[:user]
98
- req.proxy_password = proxy[:password]
99
- end
100
- end
101
-
102
- def configure_timeout(req, env)
103
- env_req = request_options(env)
104
- req.timeout = req.connect_timeout = (env_req[:timeout] * 1000) if env_req[:timeout]
105
- req.connect_timeout = (env_req[:open_timeout] * 1000) if env_req[:open_timeout]
106
- end
107
-
108
- def configure_socket(req, env)
109
- if bind = request_options(env)[:bind]
110
- req.interface = bind[:host]
111
- end
112
- end
113
-
114
- def request_options(env)
115
- env[:request]
116
- end
117
-
118
- def parallel?(env)
119
- !!env[:parallel_manager]
120
- end
12
+ dependency 'typhoeus/adapters/faraday'
121
13
  end
122
14
  end
123
15
  end