faraday 0.15.4 → 0.16.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 (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