faraday 1.0.0.pre.rc1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +276 -0
  3. data/README.md +4 -4
  4. data/Rakefile +7 -0
  5. data/UPGRADING.md +55 -0
  6. data/examples/client_spec.rb +65 -0
  7. data/examples/client_test.rb +79 -0
  8. data/lib/faraday.rb +4 -4
  9. data/lib/faraday/adapter.rb +46 -0
  10. data/lib/faraday/adapter/em_http.rb +5 -6
  11. data/lib/faraday/adapter/em_http_ssl_patch.rb +1 -1
  12. data/lib/faraday/adapter/excon.rb +24 -22
  13. data/lib/faraday/adapter/httpclient.rb +40 -39
  14. data/lib/faraday/adapter/net_http.rb +42 -38
  15. data/lib/faraday/adapter/net_http_persistent.rb +3 -1
  16. data/lib/faraday/adapter/patron.rb +42 -24
  17. data/lib/faraday/adapter/rack.rb +2 -1
  18. data/lib/faraday/connection.rb +10 -22
  19. data/lib/faraday/encoders/flat_params_encoder.rb +7 -3
  20. data/lib/faraday/error.rb +44 -12
  21. data/lib/faraday/{upload_io.rb → file_part.rb} +53 -3
  22. data/lib/faraday/logging/formatter.rb +28 -15
  23. data/lib/faraday/middleware.rb +8 -0
  24. data/lib/faraday/options.rb +1 -1
  25. data/lib/faraday/options/env.rb +1 -1
  26. data/lib/faraday/options/request_options.rb +3 -2
  27. data/lib/faraday/param_part.rb +53 -0
  28. data/lib/faraday/request/multipart.rb +9 -1
  29. data/lib/faraday/response.rb +2 -2
  30. data/lib/faraday/response/raise_error.rb +2 -0
  31. data/spec/faraday/adapter/em_http_spec.rb +47 -0
  32. data/spec/faraday/adapter/em_synchrony_spec.rb +16 -0
  33. data/spec/faraday/adapter/excon_spec.rb +49 -0
  34. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  35. data/spec/faraday/adapter/net_http_persistent_spec.rb +57 -0
  36. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  37. data/spec/faraday/adapter/patron_spec.rb +18 -0
  38. data/spec/faraday/adapter/rack_spec.rb +8 -0
  39. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  40. data/spec/faraday/adapter_registry_spec.rb +28 -0
  41. data/spec/faraday/adapter_spec.rb +55 -0
  42. data/spec/faraday/composite_read_io_spec.rb +80 -0
  43. data/spec/faraday/connection_spec.rb +691 -0
  44. data/spec/faraday/error_spec.rb +45 -0
  45. data/spec/faraday/middleware_spec.rb +26 -0
  46. data/spec/faraday/options/env_spec.rb +70 -0
  47. data/spec/faraday/options/options_spec.rb +297 -0
  48. data/spec/faraday/options/proxy_options_spec.rb +37 -0
  49. data/spec/faraday/options/request_options_spec.rb +19 -0
  50. data/spec/faraday/params_encoders/flat_spec.rb +34 -0
  51. data/spec/faraday/params_encoders/nested_spec.rb +134 -0
  52. data/spec/faraday/rack_builder_spec.rb +196 -0
  53. data/spec/faraday/request/authorization_spec.rb +88 -0
  54. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  55. data/spec/faraday/request/multipart_spec.rb +274 -0
  56. data/spec/faraday/request/retry_spec.rb +242 -0
  57. data/spec/faraday/request/url_encoded_spec.rb +70 -0
  58. data/spec/faraday/request_spec.rb +109 -0
  59. data/spec/faraday/response/logger_spec.rb +220 -0
  60. data/spec/faraday/response/middleware_spec.rb +52 -0
  61. data/spec/faraday/response/raise_error_spec.rb +106 -0
  62. data/spec/faraday/response_spec.rb +75 -0
  63. data/spec/faraday/utils/headers_spec.rb +82 -0
  64. data/spec/faraday/utils_spec.rb +56 -0
  65. data/spec/faraday_spec.rb +37 -0
  66. data/spec/spec_helper.rb +132 -0
  67. data/spec/support/disabling_stub.rb +14 -0
  68. data/spec/support/fake_safe_buffer.rb +15 -0
  69. data/spec/support/helper_methods.rb +133 -0
  70. data/spec/support/shared_examples/adapter.rb +104 -0
  71. data/spec/support/shared_examples/params_encoder.rb +18 -0
  72. data/spec/support/shared_examples/request_method.rb +234 -0
  73. data/spec/support/streaming_response_checker.rb +35 -0
  74. data/spec/support/webmock_rack_app.rb +68 -0
  75. metadata +65 -11
@@ -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
@@ -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)
@@ -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
@@ -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
@@ -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)
@@ -1,23 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Faraday namespace.
3
4
  module Faraday
4
5
  # Faraday error base class.
5
6
  class Error < StandardError
6
7
  attr_reader :response, :wrapped_exception
7
8
 
8
9
  def initialize(exc, response = nil)
9
- @wrapped_exception = nil
10
- @response = response
11
-
12
- if exc.respond_to?(:backtrace)
13
- super(exc.message)
14
- @wrapped_exception = exc
15
- elsif exc.respond_to?(:each_key)
16
- super("the server responded with status #{exc[:status]}")
17
- @response = exc
18
- else
19
- super(exc.to_s)
20
- end
10
+ @wrapped_exception = nil unless defined?(@wrapped_exception)
11
+ @response = nil unless defined?(@response)
12
+ super(exc_msg_and_response!(exc, response))
21
13
  end
22
14
 
23
15
  def backtrace
@@ -35,6 +27,38 @@ module Faraday
35
27
  inner << " #{super}" if inner.empty?
36
28
  %(#<#{self.class}#{inner}>)
37
29
  end
30
+
31
+ protected
32
+
33
+ # Pulls out potential parent exception and response hash, storing them in
34
+ # instance variables.
35
+ # exc - Either an Exception, a string message, or a response hash.
36
+ # response - Hash
37
+ # :status - Optional integer HTTP response status
38
+ # :headers - String key/value hash of HTTP response header
39
+ # values.
40
+ # :body - Optional string HTTP response body.
41
+ #
42
+ # If a subclass has to call this, then it should pass a string message
43
+ # to `super`. See NilStatusError.
44
+ def exc_msg_and_response!(exc, response = nil)
45
+ if @response.nil? && @wrapped_exception.nil?
46
+ @wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
47
+ return msg
48
+ end
49
+
50
+ exc.to_s
51
+ end
52
+
53
+ # Pulls out potential parent exception and response hash.
54
+ def exc_msg_and_response(exc, response = nil)
55
+ return [exc, exc.message, response] if exc.respond_to?(:backtrace)
56
+
57
+ return [nil, "the server responded with status #{exc[:status]}", exc] \
58
+ if exc.respond_to?(:each_key)
59
+
60
+ [nil, exc.to_s, response]
61
+ end
38
62
  end
39
63
 
40
64
  # Faraday client error class. Represents 4xx status responses.
@@ -80,6 +104,14 @@ module Faraday
80
104
  end
81
105
  end
82
106
 
107
+ # Raised by Faraday::Response::RaiseError in case of a nil status in response.
108
+ class NilStatusError < ServerError
109
+ def initialize(exc, response = nil)
110
+ exc_msg_and_response!(exc, response)
111
+ super('http status could not be derived from the server response')
112
+ end
113
+ end
114
+
83
115
  # A unified error for failed connections.
84
116
  class ConnectionFailed < Error
85
117
  end
@@ -7,6 +7,59 @@ require 'composite_io'
7
7
  require 'parts'
8
8
 
9
9
  module Faraday
10
+ # Multipart value used to POST a binary data from a file or
11
+ #
12
+ # @example
13
+ # payload = { file: Faraday::FilePart.new("file_name.ext", "content/type") }
14
+ # http.post("/upload", payload)
15
+ #
16
+
17
+ # @!method initialize(filename_or_io, content_type, filename = nil, opts = {})
18
+ #
19
+ # @param filename_or_io [String, IO] Either a String filename to a local
20
+ # file or an open IO object.
21
+ # @param content_type [String] String content type of the file data.
22
+ # @param filename [String] Optional String filename, usually to add context
23
+ # to a given IO object.
24
+ # @param opts [Hash] Optional Hash of String key/value pairs to describethis
25
+ # this uploaded file. Expected Header keys include:
26
+ # * Content-Transfer-Encoding - Defaults to "binary"
27
+ # * Content-Disposition - Defaults to "form-data"
28
+ # * Content-Type - Defaults to the content_type argument.
29
+ # * Content-ID - Optional.
30
+ #
31
+ # @return [Faraday::FilePart]
32
+ #
33
+ # @!attribute [r] content_type
34
+ # The uploaded binary data's content type.
35
+ #
36
+ # @return [String]
37
+ #
38
+ # @!attribute [r] original_filename
39
+ # The base filename, taken either from the filename_or_io or filename
40
+ # arguments in #initialize.
41
+ #
42
+ # @return [String]
43
+ #
44
+ # @!attribute [r] opts
45
+ # Extra String key/value pairs to make up the header for this uploaded file.
46
+ #
47
+ # @return [Hash]
48
+ #
49
+ # @!attribute [r] io
50
+ # The open IO object for the uploaded file.
51
+ #
52
+ # @return [IO]
53
+ FilePart = ::UploadIO
54
+
55
+ # Multipart value used to POST a file.
56
+ #
57
+ # @deprecated Use FilePart instead of this class. It behaves identically, with
58
+ # a matching name to ParamPart.
59
+ UploadIO = ::UploadIO
60
+
61
+ Parts = ::Parts
62
+
10
63
  # Similar to, but not compatible with CompositeReadIO provided by the
11
64
  # multipart-post gem.
12
65
  # https://github.com/nicksieger/multipart-post/blob/master/lib/composite_io.rb
@@ -72,7 +125,4 @@ module Faraday
72
125
  @index += 1
73
126
  end
74
127
  end
75
-
76
- UploadIO = ::UploadIO
77
- Parts = ::Parts
78
128
  end
@@ -7,7 +7,8 @@ module Faraday
7
7
  class Formatter
8
8
  extend Forwardable
9
9
 
10
- DEFAULT_OPTIONS = { headers: true, bodies: false }.freeze
10
+ DEFAULT_OPTIONS = { headers: true, bodies: false,
11
+ log_level: :info }.freeze
11
12
 
12
13
  def initialize(logger:, options:)
13
14
  @logger = logger
@@ -18,27 +19,21 @@ module Faraday
18
19
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
19
20
 
20
21
  def request(env)
21
- info('request') do
22
+ request_log = proc do
22
23
  "#{env.method.upcase} #{apply_filters(env.url.to_s)}"
23
24
  end
24
- if log_headers?(:request)
25
- debug('request') { apply_filters(dump_headers(env.request_headers)) }
26
- end
27
- return unless env[:body] && log_body?(:request)
25
+ public_send(log_level, 'request', &request_log)
28
26
 
29
- debug('request') { apply_filters(dump_body(env[:body])) }
27
+ log_headers('request', env.request_headers) if log_headers?(:request)
28
+ log_body('request', env[:body]) if env[:body] && log_body?(:request)
30
29
  end
31
30
 
32
31
  def response(env)
33
- info('response') { "Status #{env.status}" }
34
- if log_headers?(:response)
35
- debug('response') do
36
- apply_filters(dump_headers(env.response_headers))
37
- end
38
- end
39
- return unless env[:body] && log_body?(:response)
32
+ status = proc { "Status #{env.status}" }
33
+ public_send(log_level, 'response', &status)
40
34
 
41
- debug('response') { apply_filters(dump_body(env[:body])) }
35
+ log_headers('response', env.response_headers) if log_headers?(:response)
36
+ log_body('response', env[:body]) if env[:body] && log_body?(:response)
42
37
  end
43
38
 
44
39
  def filter(filter_word, filter_replacement)
@@ -87,6 +82,24 @@ module Faraday
87
82
  end
88
83
  output
89
84
  end
85
+
86
+ def log_level
87
+ unless %i[debug info warn error fatal].include?(@options[:log_level])
88
+ return :info
89
+ end
90
+
91
+ @options[:log_level]
92
+ end
93
+
94
+ def log_headers(type, headers)
95
+ headers_log = proc { apply_filters(dump_headers(headers)) }
96
+ public_send(log_level, type, &headers_log)
97
+ end
98
+
99
+ def log_body(type, body)
100
+ body_log = proc { apply_filters(dump_body(body)) }
101
+ public_send(log_level, type, &body_log)
102
+ end
90
103
  end
91
104
  end
92
105
  end
@@ -9,5 +9,13 @@ module Faraday
9
9
  def initialize(app = nil)
10
10
  @app = app
11
11
  end
12
+
13
+ def close
14
+ if @app.respond_to?(:close)
15
+ @app.close
16
+ else
17
+ warn "#{@app} does not implement \#close!"
18
+ end
19
+ end
12
20
  end
13
21
  end
@@ -79,7 +79,7 @@ module Faraday
79
79
  if !args.empty?
80
80
  send(key_setter, args.first)
81
81
  elsif block_given?
82
- send(key_setter, Proc.new.call(key))
82
+ send(key_setter, yield(key))
83
83
  else
84
84
  raise self.class.fetch_error_class, "key not found: #{key.inspect}"
85
85
  end