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
@@ -6,51 +6,54 @@ module Faraday
6
6
  class HTTPClient < Faraday::Adapter
7
7
  dependency 'httpclient'
8
8
 
9
- # @return [HTTPClient]
10
- def client
11
- @client ||= ::HTTPClient.new
12
- end
13
-
14
- def call(env)
15
- super
16
-
17
- # enable compression
18
- client.transparent_gzip_decompression = true
9
+ def build_connection(env)
10
+ @client ||= ::HTTPClient.new.tap do |cli|
11
+ # enable compression
12
+ cli.transparent_gzip_decompression = true
13
+ end
19
14
 
20
15
  if (req = env[:request])
21
16
  if (proxy = req[:proxy])
22
- configure_proxy proxy
17
+ configure_proxy @client, proxy
23
18
  end
24
19
 
25
20
  if (bind = req[:bind])
26
- configure_socket bind
21
+ configure_socket @client, bind
27
22
  end
28
23
 
29
- configure_timeouts req
24
+ configure_timeouts @client, req
30
25
  end
31
26
 
32
27
  if env[:url].scheme == 'https' && (ssl = env[:ssl])
33
- configure_ssl ssl
28
+ configure_ssl @client, ssl
34
29
  end
35
30
 
36
- configure_client
31
+ configure_client @client
32
+
33
+ @client
34
+ end
35
+
36
+ def call(env)
37
+ super
37
38
 
38
39
  # TODO: Don't stream yet.
39
40
  # https://github.com/nahi/httpclient/pull/90
40
41
  env[:body] = env[:body].read if env[:body].respond_to? :read
41
42
 
42
- resp = client.request env[:method], env[:url],
43
+ connection(env) do |http|
44
+ resp = http.request env[:method], env[:url],
43
45
  body: env[:body],
44
46
  header: env[:request_headers]
45
47
 
46
- if (req = env[:request]).stream_response?
47
- warn "Streaming downloads for #{self.class.name} " \
48
- 'are not yet implemented.'
49
- req.on_data.call(resp.body, resp.body.bytesize)
50
- end
51
- save_response env, resp.status, resp.body, resp.headers, resp.reason
48
+ if (req = env[:request]).stream_response?
49
+ warn "Streaming downloads for #{self.class.name} " \
50
+ 'are not yet implemented.'
51
+ req.on_data.call(resp.body, resp.body.bytesize)
52
+ end
53
+ save_response env, resp.status, resp.body, resp.headers, resp.reason
52
54
 
53
- @app.call env
55
+ @app.call env
56
+ end
54
57
  rescue ::HTTPClient::TimeoutError, Errno::ETIMEDOUT
55
58
  raise Faraday::TimeoutError, $ERROR_INFO
56
59
  rescue ::HTTPClient::BadResponseError => e
@@ -63,7 +66,8 @@ module Faraday
63
66
  rescue Errno::EADDRNOTAVAIL, Errno::ECONNREFUSED, IOError, SocketError
64
67
  raise Faraday::ConnectionFailed, $ERROR_INFO
65
68
  rescue StandardError => e
66
- if defined?(OpenSSL) && e.is_a?(OpenSSL::SSL::SSLError)
69
+ if defined?(::OpenSSL::SSL::SSLError) && \
70
+ e.is_a?(::OpenSSL::SSL::SSLError)
67
71
  raise Faraday::SSLError, e
68
72
  end
69
73
 
@@ -71,7 +75,7 @@ module Faraday
71
75
  end
72
76
 
73
77
  # @param bind [Hash]
74
- def configure_socket(bind)
78
+ def configure_socket(client, bind)
75
79
  client.socket_local.host = bind[:host]
76
80
  client.socket_local.port = bind[:port]
77
81
  end
@@ -79,7 +83,7 @@ module Faraday
79
83
  # Configure proxy URI and any user credentials.
80
84
  #
81
85
  # @param proxy [Hash]
82
- def configure_proxy(proxy)
86
+ def configure_proxy(client, proxy)
83
87
  client.proxy = proxy[:uri]
84
88
  return unless proxy[:user] && proxy[:password]
85
89
 
@@ -87,7 +91,7 @@ module Faraday
87
91
  end
88
92
 
89
93
  # @param ssl [Hash]
90
- def configure_ssl(ssl)
94
+ def configure_ssl(client, ssl)
91
95
  ssl_config = client.ssl_config
92
96
  ssl_config.verify_mode = ssl_verify_mode(ssl)
93
97
  ssl_config.cert_store = ssl_cert_store(ssl)
@@ -100,23 +104,21 @@ module Faraday
100
104
  end
101
105
 
102
106
  # @param req [Hash]
103
- def configure_timeouts(req)
104
- configure_timeout(req) if req[:timeout]
105
- configure_open_timeout(req) if req[:open_timeout]
106
- end
107
+ def configure_timeouts(client, req)
108
+ if (sec = request_timeout(:open, req))
109
+ client.connect_timeout = sec
110
+ end
107
111
 
108
- def configure_timeout(req)
109
- client.connect_timeout = req[:timeout]
110
- client.receive_timeout = req[:timeout]
111
- client.send_timeout = req[:timeout]
112
- end
112
+ if (sec = request_timeout(:write, req))
113
+ client.send_timeout = sec
114
+ end
115
+
116
+ return unless (sec = request_timeout(:read, req))
113
117
 
114
- def configure_open_timeout(req)
115
- client.connect_timeout = req[:open_timeout]
116
- client.send_timeout = req[:open_timeout]
118
+ client.receive_timeout = sec
117
119
  end
118
120
 
119
- def configure_client
121
+ def configure_client(client)
120
122
  @config_block&.call(client)
121
123
  end
122
124
 
@@ -16,7 +16,7 @@ module Faraday
16
16
  if @connection_options.key?(:pool_size)
17
17
  options[:pool_size] = @connection_options[:pool_size]
18
18
  end
19
- Net::HTTP::Persistent.new(options)
19
+ Net::HTTP::Persistent.new(**options)
20
20
  else
21
21
  Net::HTTP::Persistent.new('Faraday')
22
22
  end
@@ -51,7 +51,7 @@ module Faraday
51
51
 
52
52
  def perform_request(http, env)
53
53
  http.request env[:url], create_request(env)
54
- rescue Errno::ETIMEDOUT => e
54
+ rescue Errno::ETIMEDOUT, Net::OpenTimeout => e
55
55
  raise Faraday::TimeoutError, e
56
56
  rescue Net::HTTP::Persistent::Error => e
57
57
  raise Faraday::TimeoutError, e if e.message.include? 'Timeout'
@@ -73,6 +73,8 @@ module Faraday
73
73
  }.freeze
74
74
 
75
75
  def configure_ssl(http, ssl)
76
+ return unless ssl
77
+
76
78
  http_set(http, :verify_mode, ssl_verify_mode(ssl))
77
79
  http_set(http, :cert_store, ssl_cert_store(ssl))
78
80
 
@@ -6,11 +6,7 @@ module Faraday
6
6
  class Patron < Faraday::Adapter
7
7
  dependency 'patron'
8
8
 
9
- def call(env)
10
- super
11
- # TODO: support streaming requests
12
- env[:body] = env[:body].read if env[:body].respond_to? :read
13
-
9
+ def build_connection(env)
14
10
  session = ::Patron::Session.new
15
11
  @config_block&.call(session)
16
12
  if (env[:url].scheme == 'https') && env[:ssl]
@@ -18,27 +14,26 @@ module Faraday
18
14
  end
19
15
 
20
16
  if (req = env[:request])
21
- if req[:timeout]
22
- session.timeout = session.connect_timeout = req[:timeout]
23
- end
24
- session.connect_timeout = req[:open_timeout] if req[:open_timeout]
25
-
26
- if (proxy = req[:proxy])
27
- proxy_uri = proxy[:uri].dup
28
- proxy_uri.user = proxy[:user] &&
29
- Utils.escape(proxy[:user]).gsub('+', '%20')
30
- proxy_uri.password = proxy[:password] &&
31
- Utils.escape(proxy[:password]).gsub('+', '%20')
32
- session.proxy = proxy_uri.to_s
33
- end
17
+ configure_timeouts(session, req)
18
+ configure_proxy(session, req[:proxy])
34
19
  end
35
20
 
36
- response = begin
37
- data = env[:body] ? env[:body].to_s : nil
38
- session.request(env[:method], env[:url].to_s,
39
- env[:request_headers], data: data)
40
- rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
41
- raise Faraday::ConnectionFailed, $ERROR_INFO
21
+ session
22
+ end
23
+
24
+ def call(env)
25
+ super
26
+ # TODO: support streaming requests
27
+ env[:body] = env[:body].read if env[:body].respond_to? :read
28
+
29
+ response = connection(env) do |session|
30
+ begin
31
+ data = env[:body] ? env[:body].to_s : nil
32
+ session.request(env[:method], env[:url].to_s,
33
+ env[:request_headers], data: data)
34
+ rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
35
+ raise Faraday::ConnectionFailed, $ERROR_INFO
36
+ end
42
37
  end
43
38
 
44
39
  if (req = env[:request]).stream_response?
@@ -91,6 +86,29 @@ module Faraday
91
86
  end
92
87
  end
93
88
 
89
+ def configure_timeouts(session, req)
90
+ return unless req
91
+
92
+ if (sec = request_timeout(:read, req))
93
+ session.timeout = sec
94
+ end
95
+
96
+ return unless (sec = request_timeout(:open, req))
97
+
98
+ session.connect_timeout = sec
99
+ end
100
+
101
+ def configure_proxy(session, proxy)
102
+ return unless proxy
103
+
104
+ proxy_uri = proxy[:uri].dup
105
+ proxy_uri.user = proxy[:user] &&
106
+ Utils.escape(proxy[:user]).gsub('+', '%20')
107
+ proxy_uri.password = proxy[:password] &&
108
+ Utils.escape(proxy[:password]).gsub('+', '%20')
109
+ session.proxy = proxy_uri.to_s
110
+ end
111
+
94
112
  private
95
113
 
96
114
  CURL_TIMEOUT_MESSAGES = [
@@ -37,7 +37,8 @@ module Faraday
37
37
  rack_env[name] = value
38
38
  end
39
39
 
40
- timeout = env[:request][:timeout] || env[:request][:open_timeout]
40
+ timeout = request_timeout(:open, env[:request])
41
+ timeout ||= request_timeout(:read, env[:request])
41
42
  response = if timeout
42
43
  Timer.timeout(timeout, Faraday::TimeoutError) do
43
44
  execute_request(env, rack_env)
@@ -3,7 +3,7 @@
3
3
  module Faraday
4
4
  class Adapter
5
5
  # Typhoeus adapter. This class is just a stub, the real adapter is in
6
- # https://github.com/philsturgeon/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
6
+ # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
7
7
  class Typhoeus < Faraday::Adapter
8
8
  # Needs to define this method in order to support Typhoeus <= 1.3.0
9
9
  def call; end
@@ -12,7 +12,9 @@ module Faraday
12
12
  end
13
13
 
14
14
  def get(name)
15
- klass = @constants[name]
15
+ klass = @lock.synchronize do
16
+ @constants[name]
17
+ end
16
18
  return klass if klass
17
19
 
18
20
  Object.const_get(name).tap { |c| set(c, name) }
@@ -23,7 +23,7 @@ module Faraday
23
23
  #
24
24
  # @return [void]
25
25
  def autoload_all(prefix, options)
26
- if prefix =~ %r{^faraday(/|$)}i
26
+ if prefix.match? %r{^faraday(/|$)}i
27
27
  prefix = File.join(Faraday.root_path, prefix)
28
28
  end
29
29
 
@@ -58,7 +58,6 @@ module Faraday
58
58
  class Adapter
59
59
  extend AutoloadHelper
60
60
  autoload_all 'faraday/adapter',
61
- NetHttp: 'net_http',
62
61
  NetHttpPersistent: 'net_http_persistent',
63
62
  EMSynchrony: 'em_synchrony',
64
63
  EMHttp: 'em_http',
@@ -14,7 +14,7 @@ module Faraday
14
14
  #
15
15
  class Connection
16
16
  # A Set of allowed HTTP verbs.
17
- METHODS = Set.new %i[get post put delete head patch options trace connect]
17
+ METHODS = Set.new %i[get post put delete head patch options trace]
18
18
 
19
19
  # @return [Hash] URI query unencoded key/value pairs.
20
20
  attr_reader :params
@@ -99,7 +99,6 @@ module Faraday
99
99
  else
100
100
  proxy_from_env(url)
101
101
  end
102
- @temp_proxy = @proxy
103
102
  end
104
103
 
105
104
  # Sets the Hash of URI query unencoded key/value pairs.
@@ -118,6 +117,13 @@ module Faraday
118
117
 
119
118
  def_delegators :builder, :build, :use, :request, :response, :adapter, :app
120
119
 
120
+ # Closes the underlying resources and/or connections. In the case of
121
+ # persistent connections, this closes all currently open connections
122
+ # but does not prevent new connections from being made.
123
+ def close
124
+ app.close
125
+ end
126
+
121
127
  # @!method get(url = nil, params = nil, headers = nil)
122
128
  # Makes a GET HTTP request without a body.
123
129
  # @!scope class
@@ -170,21 +176,6 @@ module Faraday
170
176
  # @yield [Faraday::Request] for further request customizations
171
177
  # @return [Faraday::Response]
172
178
 
173
- # @!method connect(url = nil, params = nil, headers = nil)
174
- # Makes a CONNECT HTTP request without a body.
175
- # @!scope class
176
- #
177
- # @param url [String] The optional String base URL to use as a prefix for
178
- # all requests. Can also be the options Hash.
179
- # @param params [Hash] Hash of URI query unencoded key/value pairs.
180
- # @param headers [Hash] unencoded HTTP header key/value pairs.
181
- #
182
- # @example
183
- # conn.connect '/items/1'
184
- #
185
- # @yield [Faraday::Request] for further request customizations
186
- # @return [Faraday::Response]
187
-
188
179
  # @!method trace(url = nil, params = nil, headers = nil)
189
180
  # Makes a TRACE HTTP request without a body.
190
181
  # @!scope class
@@ -438,7 +429,7 @@ module Faraday
438
429
  # @return [String] the new path prefix
439
430
  def path_prefix=(value)
440
431
  url_prefix.path = if value
441
- value = '/' + value unless value[0, 1] == '/'
432
+ value = "/#{value}" unless value[0, 1] == '/'
442
433
  value
443
434
  end
444
435
  end
@@ -490,11 +481,8 @@ module Faraday
490
481
  raise ArgumentError, "unknown http method: #{method}"
491
482
  end
492
483
 
493
- # Resets temp_proxy
494
- @temp_proxy = proxy_for_request(url)
495
-
496
484
  request = build_request(method) do |req|
497
- req.options = req.options.merge(proxy: @temp_proxy)
485
+ req.options.proxy = proxy_for_request(url)
498
486
  req.url(url) if url
499
487
  req.headers.update(headers) if headers
500
488
  req.body = body if body
@@ -514,7 +502,7 @@ module Faraday
514
502
  Request.create(method) do |req|
515
503
  req.params = params.dup
516
504
  req.headers = headers.dup
517
- req.options = options
505
+ req.options = options.dup
518
506
  yield(req) if block_given?
519
507
  end
520
508
  end
@@ -532,7 +520,7 @@ module Faraday
532
520
  base = url_prefix
533
521
  if url && base.path && base.path !~ %r{/$}
534
522
  base = base.dup
535
- base.path = base.path + '/' # ensure trailing slash
523
+ base.path = "#{base.path}/" # ensure trailing slash
536
524
  end
537
525
  uri = url ? base + url : base
538
526
  if params
@@ -605,7 +593,7 @@ module Faraday
605
593
  uri = ENV['http_proxy']
606
594
  return unless uri && !uri.empty?
607
595
 
608
- uri = 'http://' + uri if uri !~ /^http/i
596
+ uri = "http://#{uri}" unless uri.match?(/^http/i)
609
597
  uri
610
598
  end
611
599
 
@@ -33,9 +33,9 @@ module Faraday
33
33
  key = key.to_s if key.is_a?(Symbol)
34
34
  [key, value]
35
35
  end
36
- # Useful default for OAuth and caching.
36
+
37
37
  # Only to be used for non-Array inputs. Arrays should preserve order.
38
- params.sort!
38
+ params.sort! if @sort_params
39
39
  end
40
40
 
41
41
  # The params have form [['key1', 'value1'], ['key2', 'value2']].
@@ -45,9 +45,13 @@ module Faraday
45
45
  if value.nil?
46
46
  buffer << "#{encoded_key}&"
47
47
  elsif value.is_a?(Array)
48
- value.each do |sub_value|
49
- encoded_value = escape(sub_value)
50
- buffer << "#{encoded_key}=#{encoded_value}&"
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
51
55
  end
52
56
  else
53
57
  encoded_value = escape(value)
@@ -90,5 +94,12 @@ module Faraday
90
94
  end
91
95
  end
92
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
93
104
  end
94
105
  end