faraday 1.0.0.pre.rc1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +350 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +6 -7
  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 +51 -41
  9. data/lib/faraday/adapter.rb +47 -1
  10. data/lib/faraday/adapter/em_http.rb +23 -20
  11. data/lib/faraday/adapter/em_http_ssl_patch.rb +1 -1
  12. data/lib/faraday/adapter/em_synchrony.rb +16 -13
  13. data/lib/faraday/adapter/excon.rb +26 -24
  14. data/lib/faraday/adapter/httpclient.rb +42 -40
  15. data/lib/faraday/adapter/net_http_persistent.rb +4 -2
  16. data/lib/faraday/adapter/patron.rb +42 -24
  17. data/lib/faraday/adapter/rack.rb +2 -1
  18. data/lib/faraday/adapter/typhoeus.rb +1 -1
  19. data/lib/faraday/adapter_registry.rb +3 -1
  20. data/lib/faraday/autoload.rb +1 -2
  21. data/lib/faraday/connection.rb +13 -25
  22. data/lib/faraday/encoders/flat_params_encoder.rb +16 -5
  23. data/lib/faraday/encoders/nested_params_encoder.rb +7 -2
  24. data/lib/faraday/error.rb +64 -12
  25. data/lib/faraday/{upload_io.rb → file_part.rb} +53 -3
  26. data/lib/faraday/logging/formatter.rb +28 -15
  27. data/lib/faraday/methods.rb +6 -0
  28. data/lib/faraday/middleware.rb +19 -1
  29. data/lib/faraday/options.rb +5 -9
  30. data/lib/faraday/options/env.rb +1 -1
  31. data/lib/faraday/options/request_options.rb +3 -2
  32. data/lib/faraday/param_part.rb +53 -0
  33. data/lib/faraday/rack_builder.rb +13 -12
  34. data/lib/faraday/request.rb +20 -10
  35. data/lib/faraday/request/authorization.rb +3 -1
  36. data/lib/faraday/request/multipart.rb +19 -4
  37. data/lib/faraday/request/retry.rb +2 -2
  38. data/lib/faraday/request/url_encoded.rb +3 -1
  39. data/lib/faraday/response.rb +6 -9
  40. data/lib/faraday/response/raise_error.rb +14 -1
  41. data/lib/faraday/utils.rb +11 -3
  42. data/lib/faraday/utils/headers.rb +2 -2
  43. data/lib/faraday/version.rb +5 -0
  44. data/spec/faraday/adapter/em_http_spec.rb +47 -0
  45. data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
  46. data/spec/faraday/adapter/excon_spec.rb +49 -0
  47. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  48. data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
  49. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  50. data/spec/faraday/adapter/patron_spec.rb +18 -0
  51. data/spec/faraday/adapter/rack_spec.rb +8 -0
  52. data/spec/faraday/adapter/test_spec.rb +260 -0
  53. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  54. data/spec/faraday/adapter_registry_spec.rb +28 -0
  55. data/spec/faraday/adapter_spec.rb +55 -0
  56. data/spec/faraday/composite_read_io_spec.rb +80 -0
  57. data/spec/faraday/connection_spec.rb +691 -0
  58. data/spec/faraday/error_spec.rb +60 -0
  59. data/spec/faraday/middleware_spec.rb +52 -0
  60. data/spec/faraday/options/env_spec.rb +70 -0
  61. data/spec/faraday/options/options_spec.rb +297 -0
  62. data/spec/faraday/options/proxy_options_spec.rb +37 -0
  63. data/spec/faraday/options/request_options_spec.rb +19 -0
  64. data/spec/faraday/params_encoders/flat_spec.rb +42 -0
  65. data/spec/faraday/params_encoders/nested_spec.rb +142 -0
  66. data/spec/faraday/rack_builder_spec.rb +345 -0
  67. data/spec/faraday/request/authorization_spec.rb +88 -0
  68. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  69. data/spec/faraday/request/multipart_spec.rb +302 -0
  70. data/spec/faraday/request/retry_spec.rb +242 -0
  71. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  72. data/spec/faraday/request_spec.rb +120 -0
  73. data/spec/faraday/response/logger_spec.rb +220 -0
  74. data/spec/faraday/response/middleware_spec.rb +68 -0
  75. data/spec/faraday/response/raise_error_spec.rb +169 -0
  76. data/spec/faraday/response_spec.rb +75 -0
  77. data/spec/faraday/utils/headers_spec.rb +82 -0
  78. data/spec/faraday/utils_spec.rb +56 -0
  79. data/spec/faraday_spec.rb +37 -0
  80. data/spec/spec_helper.rb +132 -0
  81. data/spec/support/disabling_stub.rb +14 -0
  82. data/spec/support/fake_safe_buffer.rb +15 -0
  83. data/spec/support/helper_methods.rb +133 -0
  84. data/spec/support/shared_examples/adapter.rb +105 -0
  85. data/spec/support/shared_examples/params_encoder.rb +18 -0
  86. data/spec/support/shared_examples/request_method.rb +262 -0
  87. data/spec/support/streaming_response_checker.rb +35 -0
  88. data/spec/support/webmock_rack_app.rb +68 -0
  89. metadata +96 -12
  90. data/lib/faraday/adapter/net_http.rb +0 -205
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requires Ruby with test-unit and faraday gems.
4
+ # ruby client_test.rb
5
+
6
+ require 'faraday'
7
+ require 'json'
8
+ require 'test/unit'
9
+
10
+ # Example API client
11
+ class Client
12
+ def initialize(conn)
13
+ @conn = conn
14
+ end
15
+
16
+ def sushi(jname)
17
+ res = @conn.get("/#{jname}")
18
+ data = JSON.parse(res.body)
19
+ data['name']
20
+ end
21
+ end
22
+
23
+ # Example API client test
24
+ class ClientTest < Test::Unit::TestCase
25
+ def test_sushi_name
26
+ stubs = Faraday::Adapter::Test::Stubs.new
27
+ stubs.get('/ebi') do |env|
28
+ # optional: you can inspect the Faraday::Env
29
+ assert_equal '/ebi', env.url.path
30
+ [
31
+ 200,
32
+ { 'Content-Type': 'application/javascript' },
33
+ '{"name": "shrimp"}'
34
+ ]
35
+ end
36
+
37
+ # uncomment to trigger stubs.verify_stubbed_calls failure
38
+ # stubs.get('/unused') { [404, {}, ''] }
39
+
40
+ cli = client(stubs)
41
+ assert_equal 'shrimp', cli.sushi('ebi')
42
+ stubs.verify_stubbed_calls
43
+ end
44
+
45
+ def test_sushi_404
46
+ stubs = Faraday::Adapter::Test::Stubs.new
47
+ stubs.get('/ebi') do
48
+ [
49
+ 404,
50
+ { 'Content-Type': 'application/javascript' },
51
+ '{}'
52
+ ]
53
+ end
54
+
55
+ cli = client(stubs)
56
+ assert_nil cli.sushi('ebi')
57
+ stubs.verify_stubbed_calls
58
+ end
59
+
60
+ def test_sushi_exception
61
+ stubs = Faraday::Adapter::Test::Stubs.new
62
+ stubs.get('/ebi') do
63
+ raise Faraday::ConnectionFailed, nil
64
+ end
65
+
66
+ cli = client(stubs)
67
+ assert_raise Faraday::ConnectionFailed do
68
+ cli.sushi('ebi')
69
+ end
70
+ stubs.verify_stubbed_calls
71
+ end
72
+
73
+ def client(stubs)
74
+ conn = Faraday.new do |builder|
75
+ builder.adapter :test, stubs
76
+ end
77
+ Client.new(conn)
78
+ end
79
+ end
@@ -1,11 +1,34 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cgi'
4
+ require 'date'
4
5
  require 'set'
5
6
  require 'forwardable'
6
7
  require 'faraday/middleware_registry'
7
8
  require 'faraday/dependency_loader'
8
9
 
10
+ unless defined?(::Faraday::Timer)
11
+ require 'timeout'
12
+ ::Faraday::Timer = Timeout
13
+ end
14
+
15
+ require 'faraday/version'
16
+ require 'faraday/methods'
17
+ require 'faraday/utils'
18
+ require 'faraday/options'
19
+ require 'faraday/connection'
20
+ require 'faraday/rack_builder'
21
+ require 'faraday/parameters'
22
+ require 'faraday/middleware'
23
+ require 'faraday/adapter'
24
+ require 'faraday/request'
25
+ require 'faraday/response'
26
+ require 'faraday/error'
27
+ require 'faraday/file_part'
28
+ require 'faraday/param_part'
29
+
30
+ require 'faraday/net_http'
31
+
9
32
  # This is the main namespace for Faraday.
10
33
  #
11
34
  # It provides methods to create {Connection} objects, and HTTP-related
@@ -19,10 +42,6 @@ require 'faraday/dependency_loader'
19
42
  # conn.get '/'
20
43
  #
21
44
  module Faraday
22
- VERSION = '1.0.0-rc1'
23
- METHODS_WITH_QUERY = %w[get head delete connect trace].freeze
24
- METHODS_WITH_BODY = %w[post put patch].freeze
25
-
26
45
  class << self
27
46
  # The root path that Faraday is being loaded from.
28
47
  #
@@ -107,6 +126,34 @@ module Faraday
107
126
  default_connection.respond_to?(symbol, include_private) || super
108
127
  end
109
128
 
129
+ # @overload default_connection
130
+ # Gets the default connection used for simple scripts.
131
+ # @return [Faraday::Connection] a connection configured with
132
+ # the default_adapter.
133
+ # @overload default_connection=(connection)
134
+ # @param connection [Faraday::Connection]
135
+ # Sets the default {Faraday::Connection} for simple scripts that
136
+ # access the Faraday constant directly, such as
137
+ # <code>Faraday.get "https://faraday.com"</code>.
138
+ def default_connection
139
+ @default_connection ||= Connection.new(default_connection_options)
140
+ end
141
+
142
+ # Gets the default connection options used when calling {Faraday#new}.
143
+ #
144
+ # @return [Faraday::ConnectionOptions]
145
+ def default_connection_options
146
+ @default_connection_options ||= ConnectionOptions.new
147
+ end
148
+
149
+ # Sets the default options used when calling {Faraday#new}.
150
+ #
151
+ # @param options [Hash, Faraday::ConnectionOptions]
152
+ def default_connection_options=(options)
153
+ @default_connection = nil
154
+ @default_connection_options = ConnectionOptions.from(options)
155
+ end
156
+
110
157
  private
111
158
 
112
159
  # Internal: Proxies method calls on the Faraday constant to
@@ -125,42 +172,5 @@ module Faraday
125
172
  self.lib_path = File.expand_path 'faraday', __dir__
126
173
  self.default_adapter = :net_http
127
174
 
128
- # @overload default_connection
129
- # Gets the default connection used for simple scripts.
130
- # @return [Faraday::Connection] a connection configured with
131
- # the default_adapter.
132
- # @overload default_connection=(connection)
133
- # @param connection [Faraday::Connection]
134
- # Sets the default {Faraday::Connection} for simple scripts that
135
- # access the Faraday constant directly, such as
136
- # <code>Faraday.get "https://faraday.com"</code>.
137
- def self.default_connection
138
- @default_connection ||= Connection.new(default_connection_options)
139
- end
140
-
141
- # Gets the default connection options used when calling {Faraday#new}.
142
- #
143
- # @return [Faraday::ConnectionOptions]
144
- def self.default_connection_options
145
- @default_connection_options ||= ConnectionOptions.new
146
- end
147
-
148
- # Sets the default options used when calling {Faraday#new}.
149
- #
150
- # @param options [Hash, Faraday::ConnectionOptions]
151
- def self.default_connection_options=(options)
152
- @default_connection = nil
153
- @default_connection_options = ConnectionOptions.from(options)
154
- end
155
-
156
- unless const_defined? :Timer
157
- require 'timeout'
158
- Timer = Timeout
159
- end
160
-
161
- require_libs 'utils', 'options', 'connection', 'rack_builder', 'parameters',
162
- 'middleware', 'adapter', 'request', 'response', 'upload_io',
163
- 'error'
164
-
165
175
  require_lib 'autoload' unless ENV['FARADAY_NO_AUTOLOAD']
166
176
  end
@@ -11,7 +11,6 @@ module Faraday
11
11
 
12
12
  register_middleware File.expand_path('adapter', __dir__),
13
13
  test: [:Test, 'test'],
14
- net_http: [:NetHttp, 'net_http'],
15
14
  net_http_persistent: [
16
15
  :NetHttpPersistent,
17
16
  'net_http_persistent'
@@ -27,6 +26,7 @@ module Faraday
27
26
  # This module marks an Adapter as supporting parallel requests.
28
27
  module Parallelism
29
28
  attr_writer :supports_parallel
29
+
30
30
  def supports_parallel?
31
31
  @supports_parallel
32
32
  end
@@ -46,6 +46,27 @@ module Faraday
46
46
  @config_block = block
47
47
  end
48
48
 
49
+ # Yields or returns an adapter's configured connection. Depends on
50
+ # #build_connection being defined on this adapter.
51
+ #
52
+ # @param env [Faraday::Env, Hash] The env object for a faraday request.
53
+ #
54
+ # @return The return value of the given block, or the HTTP connection object
55
+ # if no block is given.
56
+ def connection(env)
57
+ conn = build_connection(env)
58
+ return conn unless block_given?
59
+
60
+ yield conn
61
+ end
62
+
63
+ # Close any persistent connections. The adapter should still be usable
64
+ # after calling close.
65
+ def close
66
+ # Possible implementation:
67
+ # @app.close if @app.respond_to?(:close)
68
+ end
69
+
49
70
  def call(env)
50
71
  env.clear_body if env.needs_body?
51
72
  env.response = Response.new
@@ -65,5 +86,30 @@ module Faraday
65
86
  env.response.finish(env) unless env.parallel?
66
87
  env.response
67
88
  end
89
+
90
+ # Fetches either a read, write, or open timeout setting. Defaults to the
91
+ # :timeout value if a more specific one is not given.
92
+ #
93
+ # @param type [Symbol] Describes which timeout setting to get: :read,
94
+ # :write, or :open.
95
+ # @param options [Hash] Hash containing Symbol keys like :timeout,
96
+ # :read_timeout, :write_timeout, :open_timeout, or
97
+ # :timeout
98
+ #
99
+ # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
100
+ # has been set.
101
+ def request_timeout(type, options)
102
+ key = TIMEOUT_KEYS.fetch(type) do
103
+ msg = "Expected :read, :write, :open. Got #{type.inspect} :("
104
+ raise ArgumentError, msg
105
+ end
106
+ options[key] || options[:timeout]
107
+ end
108
+
109
+ TIMEOUT_KEYS = {
110
+ read: :read_timeout,
111
+ open: :open_timeout,
112
+ write: :write_timeout
113
+ }.freeze
68
114
  end
69
115
  end
@@ -70,10 +70,9 @@ module Faraday
70
70
 
71
71
  # Reads out timeout settings from env into options
72
72
  def configure_timeout(options, env)
73
- timeout, open_timeout = request_options(env)
74
- .values_at(:timeout, :open_timeout)
75
- options[:connect_timeout] = options[:inactivity_timeout] = timeout
76
- options[:connect_timeout] = open_timeout if open_timeout
73
+ req = request_options(env)
74
+ options[:inactivity_timeout] = request_timeout(:read, req)
75
+ options[:connect_timeout] = request_timeout(:open, req)
77
76
  end
78
77
 
79
78
  # Reads out compression header settings from env into options
@@ -91,7 +90,21 @@ module Faraday
91
90
 
92
91
  include Options
93
92
 
94
- dependency 'em-http'
93
+ dependency do
94
+ require 'em-http'
95
+
96
+ begin
97
+ require 'openssl'
98
+ rescue LoadError
99
+ warn 'Warning: no such file to load -- openssl. ' \
100
+ 'Make sure it is installed if you want HTTPS support'
101
+ else
102
+ require 'em-http/version'
103
+ if EventMachine::HttpRequest::VERSION < '1.1.6'
104
+ require 'faraday/adapter/em_http_ssl_patch'
105
+ end
106
+ end
107
+ end
95
108
 
96
109
  self.supports_parallel = true
97
110
 
@@ -143,7 +156,8 @@ module Faraday
143
156
 
144
157
  raise Faraday::ConnectionFailed, e
145
158
  rescue StandardError => e
146
- if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError)
159
+ if defined?(::OpenSSL::SSL::SSLError) && \
160
+ e.is_a?(::OpenSSL::SSL::SSLError)
147
161
  raise Faraday::SSLError, e
148
162
  end
149
163
 
@@ -229,11 +243,11 @@ module Faraday
229
243
  @running
230
244
  end
231
245
 
232
- def add
246
+ def add(&block)
233
247
  if running?
234
- perform_request { yield }
248
+ perform_request(&block)
235
249
  else
236
- @registered_procs << Proc.new
250
+ @registered_procs << block
237
251
  end
238
252
  @num_registered += 1
239
253
  end
@@ -273,14 +287,3 @@ module Faraday
273
287
  end
274
288
  end
275
289
  end
276
-
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
@@ -59,4 +59,4 @@ module EmHttpSslPatch
59
59
  end
60
60
  end
61
61
 
62
- EventMachine::HttpStubConnection.send(:include, EmHttpSslPatch)
62
+ EventMachine::HttpStubConnection.include(EmHttpSslPatch)
@@ -12,6 +12,22 @@ module Faraday
12
12
  require 'em-synchrony/em-http'
13
13
  require 'em-synchrony/em-multi'
14
14
  require 'fiber'
15
+
16
+ require 'faraday/adapter/em_synchrony/parallel_manager'
17
+
18
+ if Faraday::Adapter::EMSynchrony.loaded?
19
+ begin
20
+ require 'openssl'
21
+ rescue LoadError
22
+ warn 'Warning: no such file to load -- openssl. ' \
23
+ 'Make sure it is installed if you want HTTPS support'
24
+ else
25
+ require 'em-http/version'
26
+ if EventMachine::HttpRequest::VERSION < '1.1.6'
27
+ require 'faraday/adapter/em_http_ssl_patch'
28
+ end
29
+ end
30
+ end
15
31
  end
16
32
 
17
33
  self.supports_parallel = true
@@ -135,16 +151,3 @@ module Faraday
135
151
  end
136
152
  end
137
153
  end
138
-
139
- require 'faraday/adapter/em_synchrony/parallel_manager'
140
-
141
- if Faraday::Adapter::EMSynchrony.loaded?
142
- begin
143
- require 'openssl'
144
- rescue LoadError
145
- warn 'Warning: no such file to load -- openssl. ' \
146
- 'Make sure it is installed if you want HTTPS support'
147
- else
148
- require 'faraday/adapter/em_http_ssl_patch'
149
- end
150
- end
@@ -6,41 +6,43 @@ module Faraday
6
6
  class Excon < Faraday::Adapter
7
7
  dependency 'excon'
8
8
 
9
+ def build_connection(env)
10
+ opts = opts_from_env(env)
11
+ ::Excon.new(env[:url].to_s, opts.merge(@connection_options))
12
+ end
13
+
9
14
  def call(env)
10
15
  super
11
16
 
12
- opts = opts_from_env(env)
13
- conn = create_connection(env, opts)
14
-
15
- resp = conn.request(method: env[:method].to_s.upcase,
16
- headers: env[:request_headers],
17
- body: read_body(env))
17
+ req_opts = {
18
+ method: env[:method].to_s.upcase,
19
+ headers: env[:request_headers],
20
+ body: read_body(env)
21
+ }
18
22
 
19
23
  req = env[:request]
20
24
  if req&.stream_response?
21
- warn "Streaming downloads for #{self.class.name} are not yet " \
22
- ' implemented.'
23
- req.on_data.call(resp.body, resp.body.bytesize)
25
+ total = 0
26
+ req_opts[:response_block] = lambda do |chunk, _remain, _total|
27
+ req.on_data.call(chunk, total += chunk.size)
28
+ end
24
29
  end
30
+
31
+ resp = connection(env) { |http| http.request(req_opts) }
25
32
  save_response(env, resp.status.to_i, resp.body, resp.headers,
26
33
  resp.reason_phrase)
27
34
 
28
35
  @app.call(env)
29
36
  rescue ::Excon::Errors::SocketError => e
30
- raise Faraday::TimeoutError, e if e.message =~ /\btimeout\b/
37
+ raise Faraday::TimeoutError, e if e.message.match?(/\btimeout\b/)
31
38
 
32
- raise Faraday::SSLError, e if e.message =~ /\bcertificate\b/
39
+ raise Faraday::SSLError, e if e.message.match?(/\bcertificate\b/)
33
40
 
34
41
  raise Faraday::ConnectionFailed, e
35
42
  rescue ::Excon::Errors::Timeout => e
36
43
  raise Faraday::TimeoutError, e
37
44
  end
38
45
 
39
- # @return [Excon]
40
- def create_connection(env, opts)
41
- ::Excon.new(env[:url].to_s, opts.merge(@connection_options))
42
- end
43
-
44
46
  # TODO: support streaming requests
45
47
  def read_body(env)
46
48
  env[:body].respond_to?(:read) ? env[:body].read : env[:body]
@@ -90,17 +92,17 @@ module Faraday
90
92
  end
91
93
 
92
94
  def amend_opts_with_timeouts!(opts, req)
93
- timeout = req[:timeout]
94
- return unless timeout
95
+ if (sec = request_timeout(:read, req))
96
+ opts[:read_timeout] = sec
97
+ end
95
98
 
96
- opts[:read_timeout] = timeout
97
- opts[:connect_timeout] = timeout
98
- opts[:write_timeout] = timeout
99
+ if (sec = request_timeout(:write, req))
100
+ opts[:write_timeout] = sec
101
+ end
99
102
 
100
- open_timeout = req[:open_timeout]
101
- return unless open_timeout
103
+ return unless (sec = request_timeout(:open, req))
102
104
 
103
- opts[:connect_timeout] = open_timeout
105
+ opts[:connect_timeout] = sec
104
106
  end
105
107
 
106
108
  def amend_opts_with_proxy_settings!(opts, req)