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
@@ -21,9 +21,9 @@ module Faraday
21
21
  key = key.to_s if key.is_a?(Symbol)
22
22
  [key, value]
23
23
  end
24
- # Useful default for OAuth and caching.
24
+
25
25
  # Only to be used for non-Array inputs. Arrays should preserve order.
26
- params.sort!
26
+ params.sort! if @sort_params
27
27
  end
28
28
 
29
29
  # The params have form [['key1', 'value1'], ['key2', 'value2']].
@@ -161,10 +161,15 @@ module Faraday
161
161
  # for your requests.
162
162
  module NestedParamsEncoder
163
163
  class << self
164
+ attr_accessor :sort_params
165
+
164
166
  extend Forwardable
165
167
  def_delegators :'Faraday::Utils', :escape, :unescape
166
168
  end
167
169
 
170
+ # Useful default for OAuth and caching.
171
+ @sort_params = true
172
+
168
173
  extend EncodeMethods
169
174
  extend DecodeMethods
170
175
  end
@@ -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,58 @@ module Faraday
35
27
  inner << " #{super}" if inner.empty?
36
28
  %(#<#{self.class}#{inner}>)
37
29
  end
30
+
31
+ def response_status
32
+ @response[:status] if @response
33
+ end
34
+
35
+ def response_headers
36
+ @response[:headers] if @response
37
+ end
38
+
39
+ def response_body
40
+ @response[:body] if @response
41
+ end
42
+
43
+ protected
44
+
45
+ # Pulls out potential parent exception and response hash, storing them in
46
+ # instance variables.
47
+ # exc - Either an Exception, a string message, or a response hash.
48
+ # response - Hash
49
+ # :status - Optional integer HTTP response status
50
+ # :headers - String key/value hash of HTTP response header
51
+ # values.
52
+ # :body - Optional string HTTP response body.
53
+ # :request - Hash
54
+ # :method - Symbol with the request HTTP method.
55
+ # :url_path - String with the url path requested.
56
+ # :params - String key/value hash of query params
57
+ # present in the request.
58
+ # :headers - String key/value hash of HTTP request
59
+ # header values.
60
+ # :body - String HTTP request body.
61
+ #
62
+ # If a subclass has to call this, then it should pass a string message
63
+ # to `super`. See NilStatusError.
64
+ def exc_msg_and_response!(exc, response = nil)
65
+ if @response.nil? && @wrapped_exception.nil?
66
+ @wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
67
+ return msg
68
+ end
69
+
70
+ exc.to_s
71
+ end
72
+
73
+ # Pulls out potential parent exception and response hash.
74
+ def exc_msg_and_response(exc, response = nil)
75
+ return [exc, exc.message, response] if exc.respond_to?(:backtrace)
76
+
77
+ return [nil, "the server responded with status #{exc[:status]}", exc] \
78
+ if exc.respond_to?(:each_key)
79
+
80
+ [nil, exc.to_s, response]
81
+ end
38
82
  end
39
83
 
40
84
  # Faraday client error class. Represents 4xx status responses.
@@ -80,6 +124,14 @@ module Faraday
80
124
  end
81
125
  end
82
126
 
127
+ # Raised by Faraday::Response::RaiseError in case of a nil status in response.
128
+ class NilStatusError < ServerError
129
+ def initialize(exc, response = nil)
130
+ exc_msg_and_response!(exc, response)
131
+ super('http status could not be derived from the server response')
132
+ end
133
+ end
134
+
83
135
  # A unified error for failed connections.
84
136
  class ConnectionFailed < Error
85
137
  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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ METHODS_WITH_QUERY = %w[get head delete trace].freeze
5
+ METHODS_WITH_BODY = %w[post put patch].freeze
6
+ end
@@ -6,8 +6,26 @@ module Faraday
6
6
  extend MiddlewareRegistry
7
7
  extend DependencyLoader
8
8
 
9
- def initialize(app = nil)
9
+ attr_reader :app, :options
10
+
11
+ def initialize(app = nil, options = {})
10
12
  @app = app
13
+ @options = options
14
+ end
15
+
16
+ def call(env)
17
+ on_request(env) if respond_to?(:on_request)
18
+ app.call(env).on_complete do |environment|
19
+ on_complete(environment) if respond_to?(:on_complete)
20
+ end
21
+ end
22
+
23
+ def close
24
+ if app.respond_to?(:close)
25
+ app.close
26
+ else
27
+ warn "#{app} does not implement \#close!"
28
+ end
11
29
  end
12
30
  end
13
31
  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
@@ -103,12 +103,10 @@ module Faraday
103
103
  end
104
104
 
105
105
  # Public
106
- def each_key
106
+ def each_key(&block)
107
107
  return to_enum(:each_key) unless block_given?
108
108
 
109
- keys.each do |key|
110
- yield(key)
111
- end
109
+ keys.each(&block)
112
110
  end
113
111
 
114
112
  # Public
@@ -119,12 +117,10 @@ module Faraday
119
117
  alias has_key? key?
120
118
 
121
119
  # Public
122
- def each_value
120
+ def each_value(&block)
123
121
  return to_enum(:each_value) unless block_given?
124
122
 
125
- values.each do |value|
126
- yield(value)
127
- end
123
+ values.each(&block)
128
124
  end
129
125
 
130
126
  # Public
@@ -132,7 +132,7 @@ module Faraday
132
132
  # Sets content length to zero and the body to the empty string.
133
133
  def clear_body
134
134
  request_headers[ContentLength] = '0'
135
- self.body = ''
135
+ self.body = +''
136
136
  end
137
137
 
138
138
  # @return [Boolean] true if the status isn't in the set of
@@ -3,8 +3,9 @@
3
3
  module Faraday
4
4
  # RequestOptions contains the configurable properties for a Faraday request.
5
5
  class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
6
- :timeout, :open_timeout, :write_timeout,
7
- :boundary, :oauth, :context, :on_data)
6
+ :timeout, :open_timeout, :read_timeout,
7
+ :write_timeout, :boundary, :oauth,
8
+ :context, :on_data)
8
9
 
9
10
  def []=(key, value)
10
11
  if key && key.to_sym == :proxy
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Faraday
4
+ # Multipart value used to POST data with a content type.
5
+ class ParamPart
6
+ # @param value [String] Uploaded content as a String.
7
+ # @param content_type [String] String content type of the value.
8
+ # @param content_id [String] Optional String of this value's Content-ID.
9
+ #
10
+ # @return [Faraday::ParamPart]
11
+ def initialize(value, content_type, content_id = nil)
12
+ @value = value
13
+ @content_type = content_type
14
+ @content_id = content_id
15
+ end
16
+
17
+ # Converts this value to a form part.
18
+ #
19
+ # @param boundary [String] String multipart boundary that must not exist in
20
+ # the content exactly.
21
+ # @param key [String] String key name for this value.
22
+ #
23
+ # @return [Faraday::Parts::Part]
24
+ def to_part(boundary, key)
25
+ Faraday::Parts::Part.new(boundary, key, value, headers)
26
+ end
27
+
28
+ # Returns a Hash of String key/value pairs.
29
+ #
30
+ # @return [Hash]
31
+ def headers
32
+ {
33
+ 'Content-Type' => content_type,
34
+ 'Content-ID' => content_id
35
+ }
36
+ end
37
+
38
+ # The content to upload.
39
+ #
40
+ # @return [String]
41
+ attr_reader :value
42
+
43
+ # The value's content type.
44
+ #
45
+ # @return [String]
46
+ attr_reader :content_type
47
+
48
+ # The value's content ID, if given.
49
+ #
50
+ # @return [String, nil]
51
+ attr_reader :content_id
52
+ end
53
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'ruby2_keywords'
3
4
  require 'faraday/adapter_registry'
4
5
 
5
6
  module Faraday
@@ -27,7 +28,7 @@ module Faraday
27
28
 
28
29
  attr_reader :name
29
30
 
30
- def initialize(klass, *args, &block)
31
+ ruby2_keywords def initialize(klass, *args, &block)
31
32
  @name = klass.to_s
32
33
  REGISTRY.set(klass) if klass.respond_to?(:name)
33
34
  @args = args
@@ -89,7 +90,7 @@ module Faraday
89
90
  @handlers.frozen?
90
91
  end
91
92
 
92
- def use(klass, *args, &block)
93
+ ruby2_keywords def use(klass, *args, &block)
93
94
  if klass.is_a? Symbol
94
95
  use_symbol(Faraday::Middleware, klass, *args, &block)
95
96
  else
@@ -99,15 +100,15 @@ module Faraday
99
100
  end
100
101
  end
101
102
 
102
- def request(key, *args, &block)
103
+ ruby2_keywords def request(key, *args, &block)
103
104
  use_symbol(Faraday::Request, key, *args, &block)
104
105
  end
105
106
 
106
- def response(key, *args, &block)
107
+ ruby2_keywords def response(key, *args, &block)
107
108
  use_symbol(Faraday::Response, key, *args, &block)
108
109
  end
109
110
 
110
- def adapter(klass = NO_ARGUMENT, *args, &block)
111
+ ruby2_keywords def adapter(klass = NO_ARGUMENT, *args, &block)
111
112
  return @adapter if klass == NO_ARGUMENT
112
113
 
113
114
  klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
@@ -116,7 +117,7 @@ module Faraday
116
117
 
117
118
  ## methods to push onto the various positions in the stack:
118
119
 
119
- def insert(index, *args, &block)
120
+ ruby2_keywords def insert(index, *args, &block)
120
121
  raise_if_locked
121
122
  index = assert_index(index)
122
123
  handler = self.class::Handler.new(*args, &block)
@@ -125,12 +126,12 @@ module Faraday
125
126
 
126
127
  alias insert_before insert
127
128
 
128
- def insert_after(index, *args, &block)
129
+ ruby2_keywords def insert_after(index, *args, &block)
129
130
  index = assert_index(index)
130
131
  insert(index + 1, *args, &block)
131
132
  end
132
133
 
133
- def swap(index, *args, &block)
134
+ ruby2_keywords def swap(index, *args, &block)
134
135
  raise_if_locked
135
136
  index = assert_index(index)
136
137
  @handlers.delete_at(index)
@@ -186,7 +187,7 @@ module Faraday
186
187
  end
187
188
 
188
189
  # ENV Keys
189
- # :method - a symbolized request method (:get, :post)
190
+ # :http_method - a symbolized request HTTP method (:get, :post)
190
191
  # :body - the request body that will eventually be converted to a string.
191
192
  # :url - URI instance for the current request.
192
193
  # :status - HTTP response status code
@@ -207,7 +208,7 @@ module Faraday
207
208
  request.options.params_encoder
208
209
  )
209
210
 
210
- Env.new(request.method, request.body, exclusive_url,
211
+ Env.new(request.http_method, request.body, exclusive_url,
211
212
  request.options, request.headers, connection.ssl,
212
213
  connection.parallel_manager)
213
214
  end
@@ -231,10 +232,10 @@ module Faraday
231
232
  end
232
233
 
233
234
  def is_adapter?(klass) # rubocop:disable Naming/PredicateName
234
- klass.ancestors.include?(Faraday::Adapter)
235
+ klass <= Faraday::Adapter
235
236
  end
236
237
 
237
- def use_symbol(mod, key, *args, &block)
238
+ ruby2_keywords def use_symbol(mod, key, *args, &block)
238
239
  use(mod.lookup_middleware(key), *args, &block)
239
240
  end
240
241