faraday 0.9.1 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.md +1 -1
  3. data/README.md +189 -28
  4. data/lib/faraday/adapter/em_http.rb +8 -2
  5. data/lib/faraday/adapter/em_http_ssl_patch.rb +1 -1
  6. data/lib/faraday/adapter/em_synchrony.rb +16 -2
  7. data/lib/faraday/adapter/excon.rb +7 -8
  8. data/lib/faraday/adapter/httpclient.rb +27 -5
  9. data/lib/faraday/adapter/net_http.rb +16 -9
  10. data/lib/faraday/adapter/net_http_persistent.rb +24 -9
  11. data/lib/faraday/adapter/patron.rb +40 -12
  12. data/lib/faraday/adapter/test.rb +79 -28
  13. data/lib/faraday/adapter/typhoeus.rb +4 -115
  14. data/lib/faraday/adapter.rb +10 -1
  15. data/lib/faraday/autoload.rb +1 -1
  16. data/lib/faraday/connection.rb +72 -20
  17. data/lib/faraday/error.rb +18 -5
  18. data/lib/faraday/options.rb +40 -18
  19. data/lib/faraday/parameters.rb +54 -38
  20. data/lib/faraday/rack_builder.rb +27 -2
  21. data/lib/faraday/request/authorization.rb +1 -2
  22. data/lib/faraday/request/multipart.rb +7 -2
  23. data/lib/faraday/request/retry.rb +80 -17
  24. data/lib/faraday/request.rb +2 -0
  25. data/lib/faraday/response/logger.rb +28 -7
  26. data/lib/faraday/response.rb +6 -2
  27. data/lib/faraday/utils.rb +32 -3
  28. data/lib/faraday.rb +14 -34
  29. metadata +7 -93
  30. data/.document +0 -6
  31. data/CHANGELOG.md +0 -20
  32. data/CONTRIBUTING.md +0 -36
  33. data/Gemfile +0 -25
  34. data/Rakefile +0 -71
  35. data/faraday.gemspec +0 -34
  36. data/script/cached-bundle +0 -46
  37. data/script/console +0 -7
  38. data/script/generate_certs +0 -42
  39. data/script/package +0 -7
  40. data/script/proxy-server +0 -42
  41. data/script/release +0 -17
  42. data/script/s3-put +0 -71
  43. data/script/server +0 -36
  44. data/script/test +0 -172
  45. data/test/adapters/default_test.rb +0 -14
  46. data/test/adapters/em_http_test.rb +0 -20
  47. data/test/adapters/em_synchrony_test.rb +0 -20
  48. data/test/adapters/excon_test.rb +0 -20
  49. data/test/adapters/httpclient_test.rb +0 -21
  50. data/test/adapters/integration.rb +0 -254
  51. data/test/adapters/logger_test.rb +0 -82
  52. data/test/adapters/net_http_persistent_test.rb +0 -20
  53. data/test/adapters/net_http_test.rb +0 -14
  54. data/test/adapters/patron_test.rb +0 -20
  55. data/test/adapters/rack_test.rb +0 -31
  56. data/test/adapters/test_middleware_test.rb +0 -114
  57. data/test/adapters/typhoeus_test.rb +0 -28
  58. data/test/authentication_middleware_test.rb +0 -65
  59. data/test/composite_read_io_test.rb +0 -111
  60. data/test/connection_test.rb +0 -522
  61. data/test/env_test.rb +0 -218
  62. data/test/helper.rb +0 -81
  63. data/test/live_server.rb +0 -67
  64. data/test/middleware/instrumentation_test.rb +0 -88
  65. data/test/middleware/retry_test.rb +0 -177
  66. data/test/middleware_stack_test.rb +0 -173
  67. data/test/multibyte.txt +0 -1
  68. data/test/options_test.rb +0 -252
  69. data/test/parameters_test.rb +0 -64
  70. data/test/request_middleware_test.rb +0 -142
  71. data/test/response_middleware_test.rb +0 -72
  72. data/test/strawberry.rb +0 -2
  73. data/test/utils_test.rb +0 -58
@@ -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
@@ -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, :boundary, :oauth, :context)
194
206
 
195
207
  def []=(key, value)
196
208
  if key && key.to_sym == :proxy
@@ -231,8 +243,8 @@ module Faraday
231
243
  super(value)
232
244
  end
233
245
 
234
- memoized(:user) { uri.user && Utils.unescape(uri.user) }
235
- memoized(:password) { uri.password && Utils.unescape(uri.password) }
246
+ memoized(:user) { uri && uri.user && Utils.unescape(uri.user) }
247
+ memoized(:password) { uri && uri.password && Utils.unescape(uri.password) }
236
248
  end
237
249
 
238
250
  class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
@@ -252,7 +264,8 @@ module Faraday
252
264
  end
253
265
 
254
266
  class Env < Options.new(:method, :body, :url, :request, :request_headers,
255
- :ssl, :parallel_manager, :params, :response, :response_headers, :status)
267
+ :ssl, :parallel_manager, :params, :response, :response_headers, :status,
268
+ :reason_phrase)
256
269
 
257
270
  ContentLength = 'Content-Length'.freeze
258
271
  StatusesWithoutBody = Set.new [204, 304]
@@ -269,6 +282,15 @@ module Faraday
269
282
 
270
283
  def_delegators :request, :params_encoder
271
284
 
285
+ # Public
286
+ def self.from(value)
287
+ env = super(value)
288
+ if value.respond_to?(:custom_members)
289
+ env.custom_members.update(value.custom_members)
290
+ end
291
+ env
292
+ end
293
+
272
294
  # Public
273
295
  def [](key)
274
296
  if in_member_set?(key)
@@ -46,6 +46,8 @@ module Faraday
46
46
  buffer << "#{to_query.call(new_parent, val)}&"
47
47
  end
48
48
  return buffer.chop
49
+ elsif value.nil?
50
+ return parent
49
51
  else
50
52
  encoded_value = escape(value)
51
53
  return "#{parent}=#{encoded_value}"
@@ -63,50 +65,64 @@ module Faraday
63
65
 
64
66
  def self.decode(query)
65
67
  return nil if query == nil
66
- # Recursive helper lambda
67
- dehash = lambda do |hash|
68
- hash.each do |(key, value)|
69
- if value.kind_of?(Hash)
70
- hash[key] = dehash.call(value)
68
+
69
+ params = {}
70
+ query.split("&").each do |pair|
71
+ next if pair.empty?
72
+ key, value = pair.split("=", 2)
73
+ key = unescape(key)
74
+ value = unescape(value.gsub(/\+/, ' ')) if value
75
+
76
+ subkeys = key.scan(/[^\[\]]+(?:\]?\[\])?/)
77
+ context = params
78
+ subkeys.each_with_index do |subkey, i|
79
+ is_array = subkey =~ /[\[\]]+\Z/
80
+ subkey = $` if is_array
81
+ last_subkey = i == subkeys.length - 1
82
+
83
+ if !last_subkey || is_array
84
+ value_type = is_array ? Array : Hash
85
+ if context[subkey] && !context[subkey].is_a?(value_type)
86
+ raise TypeError, "expected %s (got %s) for param `%s'" % [
87
+ value_type.name,
88
+ context[subkey].class.name,
89
+ subkey
90
+ ]
91
+ end
92
+ context = (context[subkey] ||= value_type.new)
71
93
  end
72
- end
73
- # Numeric keys implies an array
74
- if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
75
- hash.sort.inject([]) do |accu, (_, value)|
76
- accu << value; accu
94
+
95
+ if context.is_a?(Array) && !is_array
96
+ if !context.last.is_a?(Hash) || context.last.has_key?(subkey)
97
+ context << {}
98
+ end
99
+ context = context.last
100
+ end
101
+
102
+ if last_subkey
103
+ if is_array
104
+ context << value
105
+ else
106
+ context[subkey] = value
107
+ end
77
108
  end
78
- else
79
- hash
80
109
  end
81
110
  end
82
111
 
83
- empty_accumulator = {}
84
- return ((query.split('&').map do |pair|
85
- pair.split('=', 2) if pair && !pair.empty?
86
- end).compact.inject(empty_accumulator.dup) do |accu, (key, value)|
87
- key = unescape(key)
88
- if value.kind_of?(String)
89
- value = unescape(value.gsub(/\+/, ' '))
90
- end
112
+ dehash(params, 0)
113
+ end
91
114
 
92
- array_notation = !!(key =~ /\[\]$/)
93
- subkeys = key.split(/[\[\]]+/)
94
- current_hash = accu
95
- for i in 0...(subkeys.size - 1)
96
- subkey = subkeys[i]
97
- current_hash[subkey] = {} unless current_hash[subkey]
98
- current_hash = current_hash[subkey]
99
- end
100
- if array_notation
101
- current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
102
- current_hash[subkeys.last] << value
103
- else
104
- current_hash[subkeys.last] = value
105
- end
106
- accu
107
- end).inject(empty_accumulator.dup) do |accu, (key, value)|
108
- accu[key] = value.kind_of?(Hash) ? dehash.call(value) : value
109
- accu
115
+ # Internal: convert a nested hash with purely numeric keys into an array.
116
+ # FIXME: this is not compatible with Rack::Utils.parse_nested_query
117
+ def self.dehash(hash, depth)
118
+ hash.each do |key, value|
119
+ hash[key] = dehash(value, depth + 1) if value.kind_of?(Hash)
120
+ end
121
+
122
+ if depth > 0 && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ }
123
+ hash.keys.sort.inject([]) { |all, key| all << hash[key] }
124
+ else
125
+ hash
110
126
  end
111
127
  end
112
128
  end
@@ -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
 
@@ -151,8 +155,9 @@ module Faraday
151
155
  lock!
152
156
  to_app(lambda { |env|
153
157
  response = Response.new
154
- response.finish(env) unless env.parallel?
155
158
  env.response = response
159
+ response.finish(env) unless env.parallel?
160
+ response
156
161
  })
157
162
  end
158
163
  end
@@ -188,7 +193,7 @@ module Faraday
188
193
  # :ssl - Hash of options for configuring SSL requests.
189
194
  def build_env(connection, request)
190
195
  Env.new(request.method, request.body,
191
- connection.build_exclusive_url(request.path, request.params),
196
+ connection.build_exclusive_url(request.path, request.params, request.options.params_encoder),
192
197
  request.options, request.headers, connection.ssl,
193
198
  connection.parallel_manager)
194
199
  end
@@ -199,6 +204,26 @@ module Faraday
199
204
  raise StackLocked, "can't modify middleware stack after making a request" if locked?
200
205
  end
201
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
+
202
227
  def use_symbol(mod, key, *args, &block)
203
228
  use(mod.lookup_middleware(key), *args, &block)
204
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
@@ -10,7 +10,7 @@ module Faraday
10
10
  #
11
11
  # Faraday.new do |conn|
12
12
  # conn.request :retry, max: 2, interval: 0.05,
13
- # interval_randomness: 0.5, backoff_factor: 2
13
+ # interval_randomness: 0.5, backoff_factor: 2,
14
14
  # exceptions: [CustomException, 'Timeout::Error']
15
15
  # conn.adapter ...
16
16
  # end
@@ -22,12 +22,14 @@ module Faraday
22
22
 
23
23
  IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put]
24
24
 
25
- class Options < Faraday::Options.new(:max, :interval, :interval_randomness, :backoff_factor,
26
- :exceptions, :methods, :retry_if)
25
+ class Options < Faraday::Options.new(:max, :interval, :max_interval, :interval_randomness,
26
+ :backoff_factor, :exceptions, :methods, :retry_if, :retry_block,
27
+ :retry_statuses)
28
+
27
29
  DEFAULT_CHECK = lambda { |env,exception| false }
28
30
 
29
31
  def self.from(value)
30
- if Fixnum === value
32
+ if Integer === value
31
33
  new(value)
32
34
  else
33
35
  super(value)
@@ -42,8 +44,12 @@ module Faraday
42
44
  (self[:interval] ||= 0).to_f
43
45
  end
44
46
 
47
+ def max_interval
48
+ (self[:max_interval] ||= Float::MAX).to_f
49
+ end
50
+
45
51
  def interval_randomness
46
- (self[:interval_randomness] ||= 0).to_i
52
+ (self[:interval_randomness] ||= 0).to_f
47
53
  end
48
54
 
49
55
  def backoff_factor
@@ -52,7 +58,8 @@ module Faraday
52
58
 
53
59
  def exceptions
54
60
  Array(self[:exceptions] ||= [Errno::ETIMEDOUT, 'Timeout::Error',
55
- Error::TimeoutError])
61
+ Error::TimeoutError,
62
+ Faraday::Error::RetriableResponse])
56
63
  end
57
64
 
58
65
  def methods
@@ -63,6 +70,13 @@ module Faraday
63
70
  self[:retry_if] ||= DEFAULT_CHECK
64
71
  end
65
72
 
73
+ def retry_block
74
+ self[:retry_block] ||= Proc.new {}
75
+ end
76
+
77
+ def retry_statuses
78
+ Array(self[:retry_statuses] ||= [])
79
+ end
66
80
  end
67
81
 
68
82
  # Public: Initialize middleware
@@ -73,13 +87,14 @@ module Faraday
73
87
  # interval_randomness - The maximum random interval amount expressed
74
88
  # as a float between 0 and 1 to use in addition to the
75
89
  # interval. (default: 0)
90
+ # max_interval - An upper limit for the interval (default: Float::MAX)
76
91
  # backoff_factor - The amount to multiple each successive retry's
77
92
  # interval amount by in order to provide backoff
78
93
  # (default: 1)
79
94
  # exceptions - The list of exceptions to handle. Exceptions can be
80
95
  # given as Class, Module, or String. (default:
81
- # [Errno::ETIMEDOUT, Timeout::Error,
82
- # Error::TimeoutError])
96
+ # [Errno::ETIMEDOUT, 'Timeout::Error',
97
+ # Error::TimeoutError, Faraday::Error::RetriableResponse])
83
98
  # methods - A list of HTTP methods to retry without calling retry_if. Pass
84
99
  # an empty Array to call retry_if for all exceptions.
85
100
  # (defaults to the idempotent HTTP methods in IDEMPOTENT_METHODS)
@@ -89,17 +104,21 @@ module Faraday
89
104
  # if the exception produced is non-recoverable or if the
90
105
  # the HTTP method called is not idempotent.
91
106
  # (defaults to return false)
107
+ # retry_block - block that is executed after every retry. Request environment, middleware options,
108
+ # current number of retries and the exception is passed to the block as parameters.
92
109
  def initialize(app, options = nil)
93
110
  super(app)
94
111
  @options = Options.from(options)
95
112
  @errmatch = build_exception_matcher(@options.exceptions)
96
113
  end
97
114
 
98
- def sleep_amount(retries)
99
- retry_index = @options.max - retries
100
- current_interval = @options.interval * (@options.backoff_factor ** retry_index)
101
- random_interval = rand * @options.interval_randomness.to_f * @options.interval
102
- current_interval + random_interval
115
+ def calculate_sleep_amount(retries, env)
116
+ retry_after = calculate_retry_after(env)
117
+ retry_interval = calculate_retry_interval(retries)
118
+
119
+ return if retry_after && retry_after > @options.max_interval
120
+
121
+ retry_after && retry_after >= retry_interval ? retry_after : retry_interval
103
122
  end
104
123
 
105
124
  def call(env)
@@ -107,14 +126,25 @@ module Faraday
107
126
  request_body = env[:body]
108
127
  begin
109
128
  env[:body] = request_body # after failure env[:body] is set to the response body
110
- @app.call(env)
129
+ @app.call(env).tap do |resp|
130
+ raise Faraday::Error::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
131
+ end
111
132
  rescue @errmatch => exception
112
133
  if retries > 0 && retry_request?(env, exception)
113
134
  retries -= 1
114
- sleep sleep_amount(retries + 1)
115
- retry
135
+ rewind_files(request_body)
136
+ @options.retry_block.call(env, @options, retries, exception)
137
+ if (sleep_amount = calculate_sleep_amount(retries + 1, env))
138
+ sleep sleep_amount
139
+ retry
140
+ end
141
+ end
142
+
143
+ if exception.is_a?(Faraday::Error::RetriableResponse)
144
+ exception.response
145
+ else
146
+ raise
116
147
  end
117
- raise
118
148
  end
119
149
  end
120
150
 
@@ -144,5 +174,38 @@ module Faraday
144
174
  @options.methods.include?(env[:method]) || @options.retry_if.call(env, exception)
145
175
  end
146
176
 
177
+ def rewind_files(body)
178
+ return unless body.is_a?(Hash)
179
+ body.each do |_, value|
180
+ if value.is_a? UploadIO
181
+ value.rewind
182
+ end
183
+ end
184
+ end
185
+
186
+ # MDN spec for Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
187
+ def calculate_retry_after(env)
188
+ response_headers = env[:response_headers]
189
+ return unless response_headers
190
+
191
+ retry_after_value = env[:response_headers]["Retry-After"]
192
+
193
+ # Try to parse date from the header value
194
+ begin
195
+ datetime = DateTime.rfc2822(retry_after_value)
196
+ datetime.to_time - Time.now.utc
197
+ rescue ArgumentError
198
+ retry_after_value.to_f
199
+ end
200
+ end
201
+
202
+ def calculate_retry_interval(retries)
203
+ retry_index = @options.max - retries
204
+ current_interval = @options.interval * (@options.backoff_factor ** retry_index)
205
+ current_interval = [current_interval, @options.max_interval].min
206
+ random_interval = rand * @options.interval_randomness.to_f * @options.interval
207
+
208
+ current_interval + random_interval
209
+ end
147
210
  end
148
211
  end
@@ -52,6 +52,8 @@ module Faraday
52
52
  path.query = nil
53
53
  end
54
54
  else
55
+ anchor_index = path.index('#')
56
+ path = path.slice(0, anchor_index) unless anchor_index.nil?
55
57
  path, query = path.split('?', 2)
56
58
  end
57
59
  self.path = path
@@ -4,7 +4,7 @@ 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)
@@ -12,22 +12,28 @@ module Faraday
12
12
  require 'logger'
13
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
@@ -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
@@ -61,8 +65,8 @@ module Faraday
61
65
 
62
66
  def finish(env)
63
67
  raise "response already finished" if finished?
64
- @on_complete_callbacks.each { |callback| callback.call(env) }
65
- @env = Env.from(env)
68
+ @env = env.is_a?(Env) ? env : Env.from(env)
69
+ @on_complete_callbacks.each { |callback| callback.call(@env) }
66
70
  return self
67
71
  end
68
72
 
data/lib/faraday/utils.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'thread'
2
- Faraday.require_libs 'parameters'
3
2
 
4
3
  module Faraday
5
4
  module Utils
@@ -11,12 +10,28 @@ module Faraday
11
10
  new(value)
12
11
  end
13
12
 
13
+ def self.allocate
14
+ new_self = super
15
+ new_self.initialize_names
16
+ new_self
17
+ end
18
+
14
19
  def initialize(hash = nil)
15
20
  super()
16
21
  @names = {}
17
22
  self.update(hash || {})
18
23
  end
19
24
 
25
+ def initialize_names
26
+ @names = {}
27
+ end
28
+
29
+ # on dup/clone, we need to duplicate @names hash
30
+ def initialize_copy(other)
31
+ super
32
+ @names = other.names.dup
33
+ end
34
+
20
35
  # need to synchronize concurrent writes to the shared KeyMap
21
36
  keymap_mutex = Mutex.new
22
37
 
@@ -81,6 +96,7 @@ module Faraday
81
96
 
82
97
  def replace(other)
83
98
  clear
99
+ @names.clear
84
100
  self.update other
85
101
  self
86
102
  end
@@ -89,9 +105,16 @@ module Faraday
89
105
 
90
106
  def parse(header_string)
91
107
  return unless header_string && !header_string.empty?
92
- 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.
93
116
  tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line
94
- 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
95
118
  each { |key, value|
96
119
  # join multiple values with a comma
97
120
  if self[key]
@@ -101,6 +124,12 @@ module Faraday
101
124
  end
102
125
  }
103
126
  end
127
+
128
+ protected
129
+
130
+ def names
131
+ @names
132
+ end
104
133
  end
105
134
 
106
135
  # hash with stringified keys