faraday 0.15.4 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +1 -1
  3. data/README.md +18 -344
  4. data/lib/faraday.rb +93 -175
  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 +100 -55
  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 +48 -27
  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 +67 -33
  25. data/lib/faraday/file_part.rb +128 -0
  26. data/lib/faraday/logging/formatter.rb +92 -0
  27. data/lib/faraday/middleware.rb +4 -28
  28. data/lib/faraday/middleware_registry.rb +129 -0
  29. data/lib/faraday/options.rb +35 -186
  30. data/lib/faraday/options/connection_options.rb +22 -0
  31. data/lib/faraday/options/env.rb +181 -0
  32. data/lib/faraday/options/proxy_options.rb +28 -0
  33. data/lib/faraday/options/request_options.rb +21 -0
  34. data/lib/faraday/options/ssl_options.rb +59 -0
  35. data/lib/faraday/param_part.rb +53 -0
  36. data/lib/faraday/parameters.rb +4 -197
  37. data/lib/faraday/rack_builder.rb +67 -56
  38. data/lib/faraday/request.rb +68 -36
  39. data/lib/faraday/request/authorization.rb +42 -30
  40. data/lib/faraday/request/basic_authentication.rb +14 -7
  41. data/lib/faraday/request/instrumentation.rb +45 -27
  42. data/lib/faraday/request/multipart.rb +79 -48
  43. data/lib/faraday/request/retry.rb +198 -169
  44. data/lib/faraday/request/token_authentication.rb +15 -10
  45. data/lib/faraday/request/url_encoded.rb +41 -23
  46. data/lib/faraday/response.rb +23 -16
  47. data/lib/faraday/response/logger.rb +22 -69
  48. data/lib/faraday/response/raise_error.rb +36 -14
  49. data/lib/faraday/utils.rb +28 -245
  50. data/lib/faraday/utils/headers.rb +139 -0
  51. data/lib/faraday/utils/params_hash.rb +61 -0
  52. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  53. metadata +21 -5
  54. data/lib/faraday/upload_io.rb +0 -67
@@ -1,43 +1,54 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
- # Public: This is a base class for all Faraday adapters. Adapters are
4
+ # Base class for all Faraday adapters. Adapters are
3
5
  # responsible for fulfilling a Faraday request.
4
- class Adapter < Middleware
5
- CONTENT_LENGTH = 'Content-Length'.freeze
6
-
7
- register_middleware File.expand_path('../adapter', __FILE__),
8
- :test => [:Test, 'test'],
9
- :net_http => [:NetHttp, 'net_http'],
10
- :net_http_persistent => [:NetHttpPersistent, 'net_http_persistent'],
11
- :typhoeus => [:Typhoeus, 'typhoeus'],
12
- :patron => [:Patron, 'patron'],
13
- :em_synchrony => [:EMSynchrony, 'em_synchrony'],
14
- :em_http => [:EMHttp, 'em_http'],
15
- :excon => [:Excon, 'excon'],
16
- :rack => [:Rack, 'rack'],
17
- :httpclient => [:HTTPClient, 'httpclient']
18
-
19
- # Public: This module marks an Adapter as supporting parallel requests.
6
+ class Adapter
7
+ extend MiddlewareRegistry
8
+ extend DependencyLoader
9
+
10
+ CONTENT_LENGTH = 'Content-Length'
11
+
12
+ register_middleware File.expand_path('adapter', __dir__),
13
+ test: [:Test, 'test'],
14
+ net_http: [:NetHttp, 'net_http'],
15
+ net_http_persistent: [
16
+ :NetHttpPersistent,
17
+ 'net_http_persistent'
18
+ ],
19
+ typhoeus: [:Typhoeus, 'typhoeus'],
20
+ patron: [:Patron, 'patron'],
21
+ em_synchrony: [:EMSynchrony, 'em_synchrony'],
22
+ em_http: [:EMHttp, 'em_http'],
23
+ excon: [:Excon, 'excon'],
24
+ rack: [:Rack, 'rack'],
25
+ httpclient: [:HTTPClient, 'httpclient']
26
+
27
+ # This module marks an Adapter as supporting parallel requests.
20
28
  module Parallelism
21
29
  attr_writer :supports_parallel
22
- def supports_parallel?() @supports_parallel end
30
+ def supports_parallel?
31
+ @supports_parallel
32
+ end
23
33
 
24
34
  def inherited(subclass)
25
35
  super
26
- subclass.supports_parallel = self.supports_parallel?
36
+ subclass.supports_parallel = supports_parallel?
27
37
  end
28
38
  end
29
39
 
30
40
  extend Parallelism
31
41
  self.supports_parallel = false
32
42
 
33
- def initialize(app = nil, opts = {}, &block)
34
- super(app)
43
+ def initialize(_app = nil, opts = {}, &block)
44
+ @app = ->(env) { env.response }
35
45
  @connection_options = opts
36
46
  @config_block = block
37
47
  end
38
48
 
39
49
  def call(env)
40
50
  env.clear_body if env.needs_body?
51
+ env.response = Response.new
41
52
  end
42
53
 
43
54
  private
@@ -45,11 +56,14 @@ module Faraday
45
56
  def save_response(env, status, body, headers = nil, reason_phrase = nil)
46
57
  env.status = status
47
58
  env.body = body
48
- env.reason_phrase = reason_phrase && reason_phrase.to_s.strip
59
+ env.reason_phrase = reason_phrase&.to_s&.strip
49
60
  env.response_headers = Utils::Headers.new.tap do |response_headers|
50
61
  response_headers.update headers unless headers.nil?
51
62
  yield(response_headers) if block_given?
52
63
  end
64
+
65
+ env.response.finish(env) unless env.parallel?
66
+ env.response
53
67
  end
54
68
  end
55
69
  end
@@ -1,10 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
3
- # EventMachine adapter is useful for either asynchronous requests
4
- # when in EM reactor loop or for making parallel requests in
5
+ # EventMachine adapter. This adapter is useful for either asynchronous
6
+ # requests when in an EM reactor loop, or for making parallel requests in
5
7
  # synchronous code.
6
8
  class EMHttp < Faraday::Adapter
9
+ # Options is a module containing helpers to convert the Faraday env object
10
+ # into options hashes for EMHTTP method calls.
7
11
  module Options
12
+ # @return [Hash]
8
13
  def connection_config(env)
9
14
  options = {}
10
15
  configure_proxy(options, env)
@@ -16,10 +21,10 @@ module Faraday
16
21
 
17
22
  def request_config(env)
18
23
  options = {
19
- :body => read_body(env),
20
- :head => env[:request_headers],
21
- # :keepalive => true,
22
- # :file => 'path/to/file', # stream data off disk
24
+ body: read_body(env),
25
+ head: env[:request_headers]
26
+ # keepalive: true,
27
+ # file: 'path/to/file', # stream data off disk
23
28
  }
24
29
  configure_compression(options, env)
25
30
  options
@@ -30,44 +35,53 @@ module Faraday
30
35
  body.respond_to?(:read) ? body.read : body
31
36
  end
32
37
 
38
+ # Reads out proxy settings from env into options
33
39
  def configure_proxy(options, env)
34
- if proxy = request_options(env)[:proxy]
35
- options[:proxy] = {
36
- :host => proxy[:uri].host,
37
- :port => proxy[:uri].port,
38
- :authorization => [proxy[:user], proxy[:password]]
39
- }
40
- end
40
+ proxy = request_options(env)[:proxy]
41
+ return unless proxy
42
+
43
+ options[:proxy] = {
44
+ host: proxy[:uri].host,
45
+ port: proxy[:uri].port,
46
+ authorization: [proxy[:user], proxy[:password]]
47
+ }
41
48
  end
42
49
 
50
+ # Reads out host and port settings from env into options
43
51
  def configure_socket(options, env)
44
- if bind = request_options(env)[:bind]
45
- options[:bind] = {
46
- :host => bind[:host],
47
- :port => bind[:port]
48
- }
49
- end
52
+ bind = request_options(env)[:bind]
53
+ return unless bind
54
+
55
+ options[:bind] = {
56
+ host: bind[:host],
57
+ port: bind[:port]
58
+ }
50
59
  end
51
60
 
61
+ # Reads out SSL certificate settings from env into options
52
62
  def configure_ssl(options, env)
53
- if env[:url].scheme == 'https' && env[:ssl]
54
- options[:ssl] = {
55
- :cert_chain_file => env[:ssl][:ca_file],
56
- :verify_peer => env[:ssl].fetch(:verify, true)
57
- }
58
- end
63
+ return unless env[:url].scheme == 'https' && env[:ssl]
64
+
65
+ options[:ssl] = {
66
+ cert_chain_file: env[:ssl][:ca_file],
67
+ verify_peer: env[:ssl].fetch(:verify, true)
68
+ }
59
69
  end
60
70
 
71
+ # Reads out timeout settings from env into options
61
72
  def configure_timeout(options, env)
62
- timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
73
+ timeout, open_timeout = request_options(env)
74
+ .values_at(:timeout, :open_timeout)
63
75
  options[:connect_timeout] = options[:inactivity_timeout] = timeout
64
76
  options[:connect_timeout] = open_timeout if open_timeout
65
77
  end
66
78
 
79
+ # Reads out compression header settings from env into options
67
80
  def configure_compression(options, env)
68
- if env[:method] == :get and not options[:head].key? 'accept-encoding'
69
- options[:head]['accept-encoding'] = 'gzip, compressed'
70
- end
81
+ return unless (env[:method] == :get) &&
82
+ !options[:head].key?('accept-encoding')
83
+
84
+ options[:head]['accept-encoding'] = 'gzip, compressed'
71
85
  end
72
86
 
73
87
  def request_options(env)
@@ -81,7 +95,8 @@ module Faraday
81
95
 
82
96
  self.supports_parallel = true
83
97
 
84
- def self.setup_parallel_manager(options = nil)
98
+ # @return [Manager]
99
+ def self.setup_parallel_manager(_options = nil)
85
100
  Manager.new
86
101
  end
87
102
 
@@ -94,95 +109,113 @@ module Faraday
94
109
  def perform_request(env)
95
110
  if parallel?(env)
96
111
  manager = env[:parallel_manager]
97
- manager.add {
98
- perform_single_request(env).
99
- callback { env[:response].finish(env) }
100
- }
101
- else
102
- unless EventMachine.reactor_running?
103
- error = nil
104
- # start EM, block until request is completed
105
- EventMachine.run do
106
- perform_single_request(env).
107
- callback { EventMachine.stop }.
108
- errback { |client|
109
- error = error_message(client)
110
- EventMachine.stop
111
- }
112
+ manager.add do
113
+ perform_single_request(env)
114
+ .callback { env[:response].finish(env) }
115
+ end
116
+ elsif EventMachine.reactor_running?
117
+ # EM is running: instruct upstream that this is an async request
118
+ env[:parallel_manager] = true
119
+ perform_single_request(env)
120
+ .callback { env[:response].finish(env) }
121
+ .errback do
122
+ # TODO: no way to communicate the error in async mode
123
+ raise NotImplementedError
112
124
  end
113
- raise_error(error) if error
114
- else
115
- # EM is running: instruct upstream that this is an async request
116
- env[:parallel_manager] = true
117
- perform_single_request(env).
118
- callback { env[:response].finish(env) }.
119
- errback {
120
- # TODO: no way to communicate the error in async mode
121
- raise NotImplementedError
122
- }
125
+ else
126
+ error = nil
127
+ # start EM, block until request is completed
128
+ EventMachine.run do
129
+ perform_single_request(env)
130
+ .callback { EventMachine.stop }
131
+ .errback do |client|
132
+ error = error_message(client)
133
+ EventMachine.stop
134
+ end
123
135
  end
136
+ raise_error(error) if error
124
137
  end
125
- rescue EventMachine::Connectify::CONNECTError => err
126
- if err.message.include?("Proxy Authentication Required")
127
- raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
128
- else
129
- raise Error::ConnectionFailed, err
138
+ rescue EventMachine::Connectify::CONNECTError => e
139
+ if e.message.include?('Proxy Authentication Required')
140
+ raise Faraday::ConnectionFailed,
141
+ %(407 "Proxy Authentication Required ")
130
142
  end
131
- rescue => err
132
- if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
133
- raise Faraday::SSLError, err
134
- else
135
- raise
143
+
144
+ raise Faraday::ConnectionFailed, e
145
+ rescue StandardError => e
146
+ if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError)
147
+ raise Faraday::SSLError, e
136
148
  end
149
+
150
+ raise
137
151
  end
138
152
 
139
153
  # TODO: reuse the connection to support pipelining
140
154
  def perform_single_request(env)
141
155
  req = create_request(env)
142
- req.setup_request(env[:method], request_config(env)).callback { |client|
156
+ req = req.setup_request(env[:method], request_config(env))
157
+ req.callback do |client|
158
+ if env[:request].stream_response?
159
+ warn "Streaming downloads for #{self.class.name} " \
160
+ 'are not yet implemented.'
161
+ env[:request].on_data.call(
162
+ client.response,
163
+ client.response.bytesize
164
+ )
165
+ end
143
166
  status = client.response_header.status
144
167
  reason = client.response_header.http_reason
145
- save_response(env, status, client.response, nil, reason) do |resp_headers|
168
+ save_response(env, status, client.response, nil, reason) do |headers|
146
169
  client.response_header.each do |name, value|
147
- resp_headers[name.to_sym] = value
170
+ headers[name.to_sym] = value
148
171
  end
149
172
  end
150
- }
173
+ end
151
174
  end
152
175
 
153
176
  def create_request(env)
154
- EventMachine::HttpRequest.new(env[:url], connection_config(env).merge(@connection_options))
177
+ EventMachine::HttpRequest.new(
178
+ env[:url], connection_config(env).merge(@connection_options)
179
+ )
155
180
  end
156
181
 
157
182
  def error_message(client)
158
- client.error or "request failed"
183
+ client.error || 'request failed'
159
184
  end
160
185
 
161
186
  def raise_error(msg)
162
- errklass = Faraday::Error::ClientError
163
- if msg == Errno::ETIMEDOUT
164
- errklass = Faraday::Error::TimeoutError
165
- msg = "request timed out"
187
+ error_class = Faraday::ClientError
188
+ if timeout_message?(msg)
189
+ error_class = Faraday::TimeoutError
190
+ msg = 'request timed out'
166
191
  elsif msg == Errno::ECONNREFUSED
167
- errklass = Faraday::Error::ConnectionFailed
168
- msg = "connection refused"
169
- elsif msg == "connection closed by server"
170
- errklass = Faraday::Error::ConnectionFailed
192
+ error_class = Faraday::ConnectionFailed
193
+ msg = 'connection refused'
194
+ elsif msg == 'connection closed by server'
195
+ error_class = Faraday::ConnectionFailed
171
196
  end
172
- raise errklass, msg
197
+ raise error_class, msg
173
198
  end
174
199
 
200
+ def timeout_message?(msg)
201
+ msg == Errno::ETIMEDOUT ||
202
+ (msg.is_a?(String) && msg.include?('timeout error'))
203
+ end
204
+
205
+ # @return [Boolean]
175
206
  def parallel?(env)
176
207
  !!env[:parallel_manager]
177
208
  end
178
209
 
179
- # The parallel manager is designed to start an EventMachine loop
210
+ # This parallel manager is designed to start an EventMachine loop
180
211
  # and block until all registered requests have been completed.
181
212
  class Manager
213
+ # @see reset
182
214
  def initialize
183
215
  reset
184
216
  end
185
217
 
218
+ # Re-initializes instance variables
186
219
  def reset
187
220
  @registered_procs = []
188
221
  @num_registered = 0
@@ -191,27 +224,30 @@ module Faraday
191
224
  @running = false
192
225
  end
193
226
 
194
- def running?() @running end
227
+ # @return [Boolean]
228
+ def running?
229
+ @running
230
+ end
195
231
 
196
- def add
232
+ def add(&block)
197
233
  if running?
198
234
  perform_request { yield }
199
235
  else
200
- @registered_procs << Proc.new
236
+ @registered_procs << block
201
237
  end
202
238
  @num_registered += 1
203
239
  end
204
240
 
205
241
  def run
206
- if @num_registered > 0
242
+ if @num_registered.positive?
207
243
  @running = true
208
244
  EventMachine.run do
209
245
  @registered_procs.each do |proc|
210
246
  perform_request(&proc)
211
247
  end
212
248
  end
213
- if @errors.size > 0
214
- raise Faraday::Error::ClientError, @errors.first || "connection failed"
249
+ unless @errors.empty?
250
+ raise Faraday::ClientError, @errors.first || 'connection failed'
215
251
  end
216
252
  end
217
253
  ensure
@@ -220,24 +256,31 @@ module Faraday
220
256
 
221
257
  def perform_request
222
258
  client = yield
223
- client.callback { @num_succeeded += 1; check_finished }
224
- client.errback { @errors << client.error; check_finished }
259
+ client.callback do
260
+ @num_succeeded += 1
261
+ check_finished
262
+ end
263
+ client.errback do
264
+ @errors << client.error
265
+ check_finished
266
+ end
225
267
  end
226
268
 
227
269
  def check_finished
228
- if @num_succeeded + @errors.size == @num_registered
229
- EventMachine.stop
230
- end
270
+ EventMachine.stop if @num_succeeded + @errors.size == @num_registered
231
271
  end
232
272
  end
233
273
  end
234
274
  end
235
275
  end
236
276
 
237
- begin
238
- require 'openssl'
239
- rescue LoadError
240
- warn "Warning: no such file to load -- openssl. Make sure it is installed if you want HTTPS support"
241
- else
242
- require 'faraday/adapter/em_http_ssl_patch'
243
- end if Faraday::Adapter::EMHttp.loaded?
277
+ if Faraday::Adapter::EMHttp.loaded?
278
+ begin
279
+ require 'openssl'
280
+ rescue LoadError
281
+ warn 'Warning: no such file to load -- openssl. ' \
282
+ 'Make sure it is installed if you want HTTPS support'
283
+ else
284
+ require 'faraday/adapter/em_http_ssl_patch'
285
+ end
286
+ end