faraday 0.9.2 → 0.17.6

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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +232 -0
  3. data/LICENSE.md +1 -1
  4. data/README.md +168 -18
  5. data/Rakefile +13 -0
  6. data/lib/faraday/adapter/em_http.rb +17 -11
  7. data/lib/faraday/adapter/em_http_ssl_patch.rb +1 -1
  8. data/lib/faraday/adapter/em_synchrony.rb +13 -7
  9. data/lib/faraday/adapter/excon.rb +12 -11
  10. data/lib/faraday/adapter/httpclient.rb +21 -10
  11. data/lib/faraday/adapter/net_http.rb +36 -13
  12. data/lib/faraday/adapter/net_http_persistent.rb +36 -17
  13. data/lib/faraday/adapter/patron.rb +36 -19
  14. data/lib/faraday/adapter/rack.rb +1 -1
  15. data/lib/faraday/adapter/test.rb +79 -28
  16. data/lib/faraday/adapter/typhoeus.rb +4 -115
  17. data/lib/faraday/adapter.rb +10 -1
  18. data/lib/faraday/autoload.rb +1 -1
  19. data/lib/faraday/connection.rb +64 -17
  20. data/lib/faraday/deprecate.rb +109 -0
  21. data/lib/faraday/error.rb +132 -27
  22. data/lib/faraday/options.rb +36 -22
  23. data/lib/faraday/parameters.rb +2 -1
  24. data/lib/faraday/rack_builder.rb +26 -2
  25. data/lib/faraday/request/authorization.rb +1 -2
  26. data/lib/faraday/request/multipart.rb +7 -2
  27. data/lib/faraday/request/retry.rb +77 -18
  28. data/lib/faraday/request.rb +24 -0
  29. data/lib/faraday/response/logger.rb +29 -8
  30. data/lib/faraday/response/raise_error.rb +7 -3
  31. data/lib/faraday/response.rb +7 -3
  32. data/lib/faraday/upload_io.rb +16 -6
  33. data/lib/faraday/utils.rb +19 -2
  34. data/lib/faraday.rb +15 -36
  35. data/spec/faraday/deprecate_spec.rb +147 -0
  36. data/spec/faraday/error_spec.rb +102 -0
  37. data/spec/faraday/response/raise_error_spec.rb +106 -0
  38. data/spec/spec_helper.rb +105 -0
  39. data/test/adapters/default_test.rb +14 -0
  40. data/test/adapters/em_http_test.rb +30 -0
  41. data/test/adapters/em_synchrony_test.rb +32 -0
  42. data/test/adapters/excon_test.rb +30 -0
  43. data/test/adapters/httpclient_test.rb +34 -0
  44. data/test/adapters/integration.rb +263 -0
  45. data/test/adapters/logger_test.rb +136 -0
  46. data/test/adapters/net_http_persistent_test.rb +114 -0
  47. data/test/adapters/net_http_test.rb +79 -0
  48. data/test/adapters/patron_test.rb +40 -0
  49. data/test/adapters/rack_test.rb +38 -0
  50. data/test/adapters/test_middleware_test.rb +157 -0
  51. data/test/adapters/typhoeus_test.rb +38 -0
  52. data/test/authentication_middleware_test.rb +65 -0
  53. data/test/composite_read_io_test.rb +109 -0
  54. data/test/connection_test.rb +738 -0
  55. data/test/env_test.rb +268 -0
  56. data/test/helper.rb +75 -0
  57. data/test/live_server.rb +67 -0
  58. data/test/middleware/instrumentation_test.rb +88 -0
  59. data/test/middleware/retry_test.rb +282 -0
  60. data/test/middleware_stack_test.rb +260 -0
  61. data/test/multibyte.txt +1 -0
  62. data/test/options_test.rb +333 -0
  63. data/test/parameters_test.rb +157 -0
  64. data/test/request_middleware_test.rb +126 -0
  65. data/test/response_middleware_test.rb +72 -0
  66. data/test/strawberry.rb +2 -0
  67. data/test/utils_test.rb +98 -0
  68. metadata +81 -63
@@ -18,23 +18,20 @@ module Faraday
18
18
  # Public
19
19
  def update(obj)
20
20
  obj.each do |key, value|
21
- if sub_options = self.class.options_for(key)
22
- value = sub_options.from(value) if value
23
- elsif Hash === value
24
- hash = {}
25
- value.each do |hash_key, hash_value|
26
- hash[hash_key] = hash_value
27
- end
28
- value = hash
21
+ sub_options = self.class.options_for(key)
22
+ if sub_options
23
+ new_value = sub_options.from(value) if value
24
+ elsif value.is_a?(Hash)
25
+ new_value = value.dup
26
+ else
27
+ new_value = value
29
28
  end
30
29
 
31
- self.send("#{key}=", value) unless value.nil?
30
+ self.send("#{key}=", new_value) unless new_value.nil?
32
31
  end
33
32
  self
34
33
  end
35
34
 
36
- alias merge! update
37
-
38
35
  # Public
39
36
  def delete(key)
40
37
  value = send(key)
@@ -48,8 +45,24 @@ module Faraday
48
45
  end
49
46
 
50
47
  # Public
51
- def merge(value)
52
- dup.update(value)
48
+ def merge!(other)
49
+ other.each do |key, other_value|
50
+ self_value = self.send(key)
51
+ sub_options = self.class.options_for(key)
52
+ new_value = (self_value && sub_options && other_value) ? self_value.merge(other_value) : other_value
53
+ self.send("#{key}=", new_value) unless new_value.nil?
54
+ end
55
+ self
56
+ end
57
+
58
+ # Public
59
+ def merge(other)
60
+ dup.merge!(other)
61
+ end
62
+
63
+ # Public
64
+ def deep_dup
65
+ self.class.from(self)
53
66
  end
54
67
 
55
68
  # Public
@@ -59,7 +72,7 @@ module Faraday
59
72
  if args.size > 0
60
73
  send(key_setter, args.first)
61
74
  elsif block_given?
62
- send(key_setter, Proc.new.call(key))
75
+ send(key_setter, yield(key))
63
76
  else
64
77
  raise self.class.fetch_error_class, "key not found: #{key.inspect}"
65
78
  end
@@ -149,8 +162,8 @@ module Faraday
149
162
  @attribute_options ||= {}
150
163
  end
151
164
 
152
- def self.memoized(key)
153
- memoized_attributes[key.to_sym] = Proc.new
165
+ def self.memoized(key, &block)
166
+ memoized_attributes[key.to_sym] = block
154
167
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
155
168
  def #{key}() self[:#{key}]; end
156
169
  RUBY
@@ -189,8 +202,7 @@ module Faraday
189
202
  end
190
203
 
191
204
  class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
192
- :timeout, :open_timeout, :boundary,
193
- :oauth)
205
+ :timeout, :open_timeout, :write_timeout, :boundary, :oauth, :context)
194
206
 
195
207
  def []=(key, value)
196
208
  if key && key.to_sym == :proxy
@@ -202,7 +214,8 @@ module Faraday
202
214
  end
203
215
 
204
216
  class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
205
- :cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth, :version)
217
+ :cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth,
218
+ :version, :min_version, :max_version)
206
219
 
207
220
  def verify?
208
221
  verify != false
@@ -231,8 +244,8 @@ module Faraday
231
244
  super(value)
232
245
  end
233
246
 
234
- memoized(:user) { uri.user && Utils.unescape(uri.user) }
235
- memoized(:password) { uri.password && Utils.unescape(uri.password) }
247
+ memoized(:user) { uri && uri.user && Utils.unescape(uri.user) }
248
+ memoized(:password) { uri && uri.password && Utils.unescape(uri.password) }
236
249
  end
237
250
 
238
251
  class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
@@ -252,7 +265,8 @@ module Faraday
252
265
  end
253
266
 
254
267
  class Env < Options.new(:method, :body, :url, :request, :request_headers,
255
- :ssl, :parallel_manager, :params, :response, :response_headers, :status)
268
+ :ssl, :parallel_manager, :params, :response, :response_headers, :status,
269
+ :reason_phrase)
256
270
 
257
271
  ContentLength = 'Content-Length'.freeze
258
272
  StatusesWithoutBody = Set.new [204, 304]
@@ -40,9 +40,10 @@ module Faraday
40
40
  end
41
41
  return buffer.chop
42
42
  elsif value.is_a?(Array)
43
+ new_parent = "#{parent}%5B%5D"
44
+ return new_parent if value.empty?
43
45
  buffer = ""
44
46
  value.each_with_index do |val, i|
45
- new_parent = "#{parent}%5B%5D"
46
47
  buffer << "#{to_query.call(new_parent, val)}&"
47
48
  end
48
49
  return buffer.chop
@@ -49,10 +49,10 @@ module Faraday
49
49
  end
50
50
  end
51
51
 
52
- def initialize(handlers = [])
52
+ def initialize(handlers = [], &block)
53
53
  @handlers = handlers
54
54
  if block_given?
55
- build(&Proc.new)
55
+ build(&block)
56
56
  elsif @handlers.empty?
57
57
  # default stack, if nothing else is configured
58
58
  self.request :url_encoded
@@ -84,6 +84,7 @@ module Faraday
84
84
  use_symbol(Faraday::Middleware, klass, *args, &block)
85
85
  else
86
86
  raise_if_locked
87
+ warn_middleware_after_adapter if adapter_set?
87
88
  @handlers << self.class::Handler.new(klass, *args, &block)
88
89
  end
89
90
  end
@@ -105,6 +106,7 @@ module Faraday
105
106
  def insert(index, *args, &block)
106
107
  raise_if_locked
107
108
  index = assert_index(index)
109
+ warn_middleware_after_adapter if inserting_after_adapter?(index)
108
110
  handler = self.class::Handler.new(*args, &block)
109
111
  @handlers.insert(index, handler)
110
112
  end
@@ -136,6 +138,8 @@ module Faraday
136
138
  #
137
139
  # Returns a Faraday::Response.
138
140
  def build_response(connection, request)
141
+ warn 'WARNING: No adapter was configured for this request' unless adapter_set?
142
+
139
143
  app.call(build_env(connection, request))
140
144
  end
141
145
 
@@ -200,6 +204,26 @@ module Faraday
200
204
  raise StackLocked, "can't modify middleware stack after making a request" if locked?
201
205
  end
202
206
 
207
+ def warn_middleware_after_adapter
208
+ warn "WARNING: Unexpected middleware set after the adapter. " \
209
+ "This won't be supported from Faraday 1.0."
210
+ end
211
+
212
+ def adapter_set?
213
+ @handlers.any? { |handler| is_adapter?(handler) }
214
+ end
215
+
216
+ def inserting_after_adapter?(index)
217
+ adapter_index = @handlers.find_index { |handler| is_adapter?(handler) }
218
+ return false if adapter_index.nil?
219
+
220
+ index > adapter_index
221
+ end
222
+
223
+ def is_adapter?(handler)
224
+ handler.klass.ancestors.include? Faraday::Adapter
225
+ end
226
+
203
227
  def use_symbol(mod, key, *args, &block)
204
228
  use(mod.lookup_middleware(key), *args, &block)
205
229
  end
@@ -16,8 +16,7 @@ module Faraday
16
16
 
17
17
  # Internal
18
18
  def self.build_hash(type, hash)
19
- offset = KEY.size + type.size + 3
20
- comma = ",\n#{' ' * offset}"
19
+ comma = ", "
21
20
  values = []
22
21
  hash.each do |key, value|
23
22
  values << "#{key}=#{value.to_s.inspect}"
@@ -1,13 +1,14 @@
1
1
  require File.expand_path("../url_encoded", __FILE__)
2
+ require 'securerandom'
2
3
 
3
4
  module Faraday
4
5
  class Request::Multipart < Request::UrlEncoded
5
6
  self.mime_type = 'multipart/form-data'.freeze
6
- DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze unless defined? DEFAULT_BOUNDARY
7
+ DEFAULT_BOUNDARY_PREFIX = "-----------RubyMultipartPost".freeze unless defined? DEFAULT_BOUNDARY_PREFIX
7
8
 
8
9
  def call(env)
9
10
  match_content_type(env) do |params|
10
- env.request.boundary ||= DEFAULT_BOUNDARY
11
+ env.request.boundary ||= unique_boundary
11
12
  env.request_headers[CONTENT_TYPE] += "; boundary=#{env.request.boundary}"
12
13
  env.body = create_multipart(env, params)
13
14
  end
@@ -44,6 +45,10 @@ module Faraday
44
45
  return body
45
46
  end
46
47
 
48
+ def unique_boundary
49
+ "#{DEFAULT_BOUNDARY_PREFIX}-#{SecureRandom.hex}"
50
+ end
51
+
47
52
  def process_params(params, prefix = nil, pieces = nil, &block)
48
53
  params.inject(pieces || []) do |all, (key, value)|
49
54
  key = "#{prefix}[#{key}]" if prefix
@@ -1,3 +1,5 @@
1
+ require 'date'
2
+
1
3
  module Faraday
2
4
  # Catches exceptions and retries each request a limited number of times.
3
5
  #
@@ -10,7 +12,7 @@ module Faraday
10
12
  #
11
13
  # Faraday.new do |conn|
12
14
  # conn.request :retry, max: 2, interval: 0.05,
13
- # interval_randomness: 0.5, backoff_factor: 2
15
+ # interval_randomness: 0.5, backoff_factor: 2,
14
16
  # exceptions: [CustomException, 'Timeout::Error']
15
17
  # conn.adapter ...
16
18
  # end
@@ -19,15 +21,19 @@ module Faraday
19
21
  # interval that is random between 0.1 and 0.15
20
22
  #
21
23
  class Request::Retry < Faraday::Middleware
22
-
24
+ DEFAULT_EXCEPTIONS = [Errno::ETIMEDOUT, 'Timeout::Error',
25
+ Faraday::TimeoutError, Faraday::RetriableResponse
26
+ ].freeze
23
27
  IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put]
24
28
 
25
29
  class Options < Faraday::Options.new(:max, :interval, :max_interval, :interval_randomness,
26
- :backoff_factor, :exceptions, :methods, :retry_if)
30
+ :backoff_factor, :exceptions, :methods, :retry_if, :retry_block,
31
+ :retry_statuses)
32
+
27
33
  DEFAULT_CHECK = lambda { |env,exception| false }
28
34
 
29
35
  def self.from(value)
30
- if Fixnum === value
36
+ if Integer === value
31
37
  new(value)
32
38
  else
33
39
  super(value)
@@ -55,8 +61,7 @@ module Faraday
55
61
  end
56
62
 
57
63
  def exceptions
58
- Array(self[:exceptions] ||= [Errno::ETIMEDOUT, 'Timeout::Error',
59
- Error::TimeoutError])
64
+ Array(self[:exceptions] ||= DEFAULT_EXCEPTIONS)
60
65
  end
61
66
 
62
67
  def methods
@@ -67,6 +72,13 @@ module Faraday
67
72
  self[:retry_if] ||= DEFAULT_CHECK
68
73
  end
69
74
 
75
+ def retry_block
76
+ self[:retry_block] ||= Proc.new {}
77
+ end
78
+
79
+ def retry_statuses
80
+ Array(self[:retry_statuses] ||= [])
81
+ end
70
82
  end
71
83
 
72
84
  # Public: Initialize middleware
@@ -83,8 +95,8 @@ module Faraday
83
95
  # (default: 1)
84
96
  # exceptions - The list of exceptions to handle. Exceptions can be
85
97
  # given as Class, Module, or String. (default:
86
- # [Errno::ETIMEDOUT, Timeout::Error,
87
- # Error::TimeoutError])
98
+ # [Errno::ETIMEDOUT, 'Timeout::Error',
99
+ # Faraday::TimeoutError, Faraday::RetriableResponse])
88
100
  # methods - A list of HTTP methods to retry without calling retry_if. Pass
89
101
  # an empty Array to call retry_if for all exceptions.
90
102
  # (defaults to the idempotent HTTP methods in IDEMPOTENT_METHODS)
@@ -94,18 +106,21 @@ module Faraday
94
106
  # if the exception produced is non-recoverable or if the
95
107
  # the HTTP method called is not idempotent.
96
108
  # (defaults to return false)
109
+ # retry_block - block that is executed after every retry. Request environment, middleware options,
110
+ # current number of retries and the exception is passed to the block as parameters.
97
111
  def initialize(app, options = nil)
98
112
  super(app)
99
113
  @options = Options.from(options)
100
114
  @errmatch = build_exception_matcher(@options.exceptions)
101
115
  end
102
116
 
103
- def sleep_amount(retries)
104
- retry_index = @options.max - retries
105
- current_interval = @options.interval * (@options.backoff_factor ** retry_index)
106
- current_interval = [current_interval, @options.max_interval].min
107
- random_interval = rand * @options.interval_randomness.to_f * @options.interval
108
- current_interval + random_interval
117
+ def calculate_sleep_amount(retries, env)
118
+ retry_after = calculate_retry_after(env)
119
+ retry_interval = calculate_retry_interval(retries)
120
+
121
+ return if retry_after && retry_after > @options.max_interval
122
+
123
+ retry_after && retry_after >= retry_interval ? retry_after : retry_interval
109
124
  end
110
125
 
111
126
  def call(env)
@@ -113,14 +128,25 @@ module Faraday
113
128
  request_body = env[:body]
114
129
  begin
115
130
  env[:body] = request_body # after failure env[:body] is set to the response body
116
- @app.call(env)
131
+ @app.call(env).tap do |resp|
132
+ raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
133
+ end
117
134
  rescue @errmatch => exception
118
135
  if retries > 0 && retry_request?(env, exception)
119
136
  retries -= 1
120
- sleep sleep_amount(retries + 1)
121
- retry
137
+ rewind_files(request_body)
138
+ @options.retry_block.call(env, @options, retries, exception)
139
+ if (sleep_amount = calculate_sleep_amount(retries + 1, env))
140
+ sleep sleep_amount
141
+ retry
142
+ end
143
+ end
144
+
145
+ if exception.is_a?(Faraday::RetriableResponse)
146
+ exception.response
147
+ else
148
+ raise
122
149
  end
123
- raise
124
150
  end
125
151
  end
126
152
 
@@ -150,5 +176,38 @@ module Faraday
150
176
  @options.methods.include?(env[:method]) || @options.retry_if.call(env, exception)
151
177
  end
152
178
 
179
+ def rewind_files(body)
180
+ return unless body.is_a?(Hash)
181
+ body.each do |_, value|
182
+ if value.is_a? UploadIO
183
+ value.rewind
184
+ end
185
+ end
186
+ end
187
+
188
+ # MDN spec for Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
189
+ def calculate_retry_after(env)
190
+ response_headers = env[:response_headers]
191
+ return unless response_headers
192
+
193
+ retry_after_value = env[:response_headers]["Retry-After"]
194
+
195
+ # Try to parse date from the header value
196
+ begin
197
+ datetime = DateTime.rfc2822(retry_after_value)
198
+ datetime.to_time - Time.now.utc
199
+ rescue ArgumentError
200
+ retry_after_value.to_f
201
+ end
202
+ end
203
+
204
+ def calculate_retry_interval(retries)
205
+ retry_index = @options.max - retries
206
+ current_interval = @options.interval * (@options.backoff_factor ** retry_index)
207
+ current_interval = [current_interval, @options.max_interval].min
208
+ random_interval = rand * @options.interval_randomness.to_f * @options.interval
209
+
210
+ current_interval + random_interval
211
+ end
153
212
  end
154
213
  end
@@ -12,6 +12,8 @@ module Faraday
12
12
  class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
13
13
  extend MiddlewareRegistry
14
14
 
15
+ alias_method :http_method, :method
16
+
15
17
  register_middleware File.expand_path('../request', __FILE__),
16
18
  :url_encoded => [:UrlEncoded, 'url_encoded'],
17
19
  :multipart => [:Multipart, 'multipart'],
@@ -52,6 +54,8 @@ module Faraday
52
54
  path.query = nil
53
55
  end
54
56
  else
57
+ anchor_index = path.index('#')
58
+ path = path.slice(0, anchor_index) unless anchor_index.nil?
55
59
  path, query = path.split('?', 2)
56
60
  end
57
61
  self.path = path
@@ -67,6 +71,26 @@ module Faraday
67
71
  headers[key] = value
68
72
  end
69
73
 
74
+ def marshal_dump
75
+ {
76
+ :method => method,
77
+ :body => body,
78
+ :headers => headers,
79
+ :path => path,
80
+ :params => params,
81
+ :options => options
82
+ }
83
+ end
84
+
85
+ def marshal_load(serialised)
86
+ self.method = serialised[:method]
87
+ self.body = serialised[:body]
88
+ self.headers = serialised[:headers]
89
+ self.path = serialised[:path]
90
+ self.params = serialised[:params]
91
+ self.options = serialised[:options]
92
+ end
93
+
70
94
  # ENV Keys
71
95
  # :method - a symbolized request method (:get, :post)
72
96
  # :body - the request body that will eventually be converted to a string.
@@ -4,30 +4,36 @@ module Faraday
4
4
  class Response::Logger < Response::Middleware
5
5
  extend Forwardable
6
6
 
7
- DEFAULT_OPTIONS = { :bodies => false }
7
+ DEFAULT_OPTIONS = { :headers => true, :bodies => false }
8
8
 
9
9
  def initialize(app, logger = nil, options = {})
10
10
  super(app)
11
11
  @logger = logger || begin
12
12
  require 'logger'
13
- ::Logger.new(STDOUT)
13
+ ::Logger.new($stdout)
14
14
  end
15
+ @filter = []
15
16
  @options = DEFAULT_OPTIONS.merge(options)
17
+ yield self if block_given?
16
18
  end
17
19
 
18
20
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
19
21
 
20
22
  def call(env)
21
- info "#{env.method} #{env.url.to_s}"
22
- debug('request') { dump_headers env.request_headers }
23
- debug('request') { dump_body(env[:body]) } if env[:body] && log_body?(:request)
23
+ info('request') { "#{env.method.upcase} #{apply_filters(env.url.to_s)}" }
24
+ debug('request') { apply_filters( dump_headers env.request_headers ) } if log_headers?(:request)
25
+ debug('request') { apply_filters( dump_body(env[:body]) ) } if env[:body] && log_body?(:request)
24
26
  super
25
27
  end
26
28
 
27
29
  def on_complete(env)
28
- info('Status') { env.status.to_s }
29
- debug('response') { dump_headers env.response_headers }
30
- debug('response') { dump_body env[:body] } if env[:body] && log_body?(:response)
30
+ info('response') { "Status #{env.status.to_s}" }
31
+ debug('response') { apply_filters( dump_headers env.response_headers ) } if log_headers?(:response)
32
+ debug('response') { apply_filters( dump_body env[:body] ) } if env[:body] && log_body?(:response)
33
+ end
34
+
35
+ def filter(filter_word, filter_replacement)
36
+ @filter.push([ filter_word, filter_replacement ])
31
37
  end
32
38
 
33
39
  private
@@ -49,11 +55,26 @@ module Faraday
49
55
  body.pretty_inspect
50
56
  end
51
57
 
58
+ def log_headers?(type)
59
+ case @options[:headers]
60
+ when Hash then @options[:headers][type]
61
+ else @options[:headers]
62
+ end
63
+ end
64
+
52
65
  def log_body?(type)
53
66
  case @options[:bodies]
54
67
  when Hash then @options[:bodies][type]
55
68
  else @options[:bodies]
56
69
  end
57
70
  end
71
+
72
+ def apply_filters(output)
73
+ @filter.each do |pattern, replacement|
74
+ output = output.to_s.gsub(pattern, replacement)
75
+ end
76
+ output
77
+ end
78
+
58
79
  end
59
80
  end
@@ -5,12 +5,16 @@ module Faraday
5
5
  def on_complete(env)
6
6
  case env[:status]
7
7
  when 404
8
- raise Faraday::Error::ResourceNotFound, response_values(env)
8
+ raise Faraday::ResourceNotFound, response_values(env)
9
9
  when 407
10
10
  # mimic the behavior that we get with proxy requests with HTTPS
11
- raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
11
+ raise Faraday::ConnectionFailed.new(
12
+ %{407 "Proxy Authentication Required "},
13
+ response_values(env))
12
14
  when ClientErrorStatuses
13
- raise Faraday::Error::ClientError, response_values(env)
15
+ raise Faraday::ClientError, response_values(env)
16
+ when nil
17
+ raise Faraday::NilStatusError, response_values(env)
14
18
  end
15
19
  end
16
20
 
@@ -37,6 +37,10 @@ module Faraday
37
37
  finished? ? env.status : nil
38
38
  end
39
39
 
40
+ def reason_phrase
41
+ finished? ? env.reason_phrase : nil
42
+ end
43
+
40
44
  def headers
41
45
  finished? ? env.response_headers : {}
42
46
  end
@@ -50,9 +54,9 @@ module Faraday
50
54
  !!env
51
55
  end
52
56
 
53
- def on_complete
54
- if not finished?
55
- @on_complete_callbacks << Proc.new
57
+ def on_complete(&block)
58
+ if !finished?
59
+ @on_complete_callbacks << block
56
60
  else
57
61
  yield(env)
58
62
  end
@@ -1,10 +1,15 @@
1
1
  begin
2
- require 'composite_io'
3
- require 'parts'
2
+ require 'multipart/post'
4
3
  require 'stringio'
5
4
  rescue LoadError
6
- $stderr.puts "Install the multipart-post gem."
7
- raise
5
+ begin
6
+ require 'composite_io'
7
+ require 'parts'
8
+ require 'stringio'
9
+ rescue LoadError
10
+ $stderr.puts "Install the multipart-post gem."
11
+ raise
12
+ end
8
13
  end
9
14
 
10
15
  module Faraday
@@ -62,6 +67,11 @@ module Faraday
62
67
  end
63
68
  end
64
69
 
65
- UploadIO = ::UploadIO
66
- Parts = ::Parts
70
+ if defined?(::Multipart::Post::UploadIO)
71
+ UploadIO = ::Multipart::Post::UploadIO
72
+ Parts = ::Multipart::Post::Parts
73
+ else
74
+ UploadIO = ::UploadIO
75
+ Parts = ::Parts
76
+ end
67
77
  end
data/lib/faraday/utils.rb CHANGED
@@ -10,12 +10,22 @@ module Faraday
10
10
  new(value)
11
11
  end
12
12
 
13
+ def self.allocate
14
+ new_self = super
15
+ new_self.initialize_names
16
+ new_self
17
+ end
18
+
13
19
  def initialize(hash = nil)
14
20
  super()
15
21
  @names = {}
16
22
  self.update(hash || {})
17
23
  end
18
24
 
25
+ def initialize_names
26
+ @names = {}
27
+ end
28
+
19
29
  # on dup/clone, we need to duplicate @names hash
20
30
  def initialize_copy(other)
21
31
  super
@@ -95,9 +105,16 @@ module Faraday
95
105
 
96
106
  def parse(header_string)
97
107
  return unless header_string && !header_string.empty?
98
- header_string.split(/\r\n/).
108
+
109
+ headers = header_string.split(/\r\n/)
110
+
111
+ # Find the last set of response headers.
112
+ start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0
113
+ last_response = headers.slice(start_index, headers.size)
114
+
115
+ last_response.
99
116
  tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
100
- map { |h| h.split(/:\s+/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines
117
+ map { |h| h.split(/:\s*/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines
101
118
  each { |key, value|
102
119
  # join multiple values with a comma
103
120
  if self[key]