faraday 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. data/LICENSE.md +1 -1
  2. data/README.md +41 -16
  3. data/lib/faraday.rb +1 -1
  4. data/lib/faraday/adapter/em_synchrony.rb +8 -0
  5. data/lib/faraday/adapter/excon.rb +1 -0
  6. data/lib/faraday/adapter/httpclient.rb +14 -3
  7. data/lib/faraday/adapter/net_http.rb +2 -2
  8. data/lib/faraday/adapter/net_http_persistent.rb +2 -1
  9. data/lib/faraday/adapter/patron.rb +8 -2
  10. data/lib/faraday/connection.rb +8 -3
  11. data/lib/faraday/options.rb +9 -0
  12. data/lib/faraday/parameters.rb +54 -38
  13. data/lib/faraday/rack_builder.rb +3 -2
  14. data/lib/faraday/request/retry.rb +9 -3
  15. data/lib/faraday/response.rb +2 -2
  16. data/lib/faraday/utils.rb +13 -1
  17. metadata +61 -124
  18. checksums.yaml +0 -7
  19. data/.document +0 -6
  20. data/CHANGELOG.md +0 -20
  21. data/CONTRIBUTING.md +0 -36
  22. data/Gemfile +0 -25
  23. data/Rakefile +0 -71
  24. data/faraday.gemspec +0 -34
  25. data/script/cached-bundle +0 -46
  26. data/script/console +0 -7
  27. data/script/generate_certs +0 -42
  28. data/script/package +0 -7
  29. data/script/proxy-server +0 -42
  30. data/script/release +0 -17
  31. data/script/s3-put +0 -71
  32. data/script/server +0 -36
  33. data/script/test +0 -172
  34. data/test/adapters/default_test.rb +0 -14
  35. data/test/adapters/em_http_test.rb +0 -20
  36. data/test/adapters/em_synchrony_test.rb +0 -20
  37. data/test/adapters/excon_test.rb +0 -20
  38. data/test/adapters/httpclient_test.rb +0 -21
  39. data/test/adapters/integration.rb +0 -254
  40. data/test/adapters/logger_test.rb +0 -82
  41. data/test/adapters/net_http_persistent_test.rb +0 -20
  42. data/test/adapters/net_http_test.rb +0 -14
  43. data/test/adapters/patron_test.rb +0 -20
  44. data/test/adapters/rack_test.rb +0 -31
  45. data/test/adapters/test_middleware_test.rb +0 -114
  46. data/test/adapters/typhoeus_test.rb +0 -28
  47. data/test/authentication_middleware_test.rb +0 -65
  48. data/test/composite_read_io_test.rb +0 -111
  49. data/test/connection_test.rb +0 -522
  50. data/test/env_test.rb +0 -218
  51. data/test/helper.rb +0 -81
  52. data/test/live_server.rb +0 -67
  53. data/test/middleware/instrumentation_test.rb +0 -88
  54. data/test/middleware/retry_test.rb +0 -177
  55. data/test/middleware_stack_test.rb +0 -173
  56. data/test/multibyte.txt +0 -1
  57. data/test/options_test.rb +0 -252
  58. data/test/parameters_test.rb +0 -64
  59. data/test/request_middleware_test.rb +0 -142
  60. data/test/response_middleware_test.rb +0 -72
  61. data/test/strawberry.rb +0 -2
  62. data/test/utils_test.rb +0 -58
data/LICENSE.md CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2013 Rick Olson, Zack Hobson
1
+ Copyright (c) 2009-2015 Rick Olson, Zack Hobson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -6,7 +6,8 @@ processing the request/response cycle.
6
6
 
7
7
  Faraday supports these adapters:
8
8
 
9
- * Net::HTTP
9
+ * [Net::HTTP][net_http] _(default)_
10
+ * [Net::HTTP::Persistent][persistent]
10
11
  * [Excon][]
11
12
  * [Typhoeus][]
12
13
  * [Patron][]
@@ -64,6 +65,32 @@ stack and default adapter (see [Faraday::RackBuilder#initialize](https://github.
64
65
  response = Faraday.get 'http://sushi.com/nigiri/sake.json'
65
66
  ```
66
67
 
68
+ ### Changing how parameters are serialized
69
+
70
+ Sometimes you need to send the same URL parameter multiple times with different
71
+ values. This requires manually setting the parameter encoder and can be done on
72
+ either per-connection or per-request basis.
73
+
74
+ ```ruby
75
+ # per-connection setting
76
+ conn = Faraday.new :params_encoder => Faraday::FlatParamsEncoder
77
+
78
+ conn.get do |req|
79
+ # per-request setting:
80
+ # req.options.params_encoder = my_encoder
81
+ req.params['roll'] = ['california', 'philadelphia']
82
+ end
83
+ # GET 'http://sushi.com?roll=california&roll=philadelphia'
84
+ ```
85
+
86
+ The value of Faraday `params_encoder` can be any object that responds to:
87
+
88
+ * `encode(hash) #=> String`
89
+ * `decode(string) #=> Hash`
90
+
91
+ The encoder will affect both how query strings are processed and how POST bodies
92
+ get serialized. The default encoder is Faraday::NestedParamsEncoder.
93
+
67
94
  ## Advanced middleware usage
68
95
 
69
96
  The order in which middleware is stacked is important. Like with Rack, the
@@ -183,13 +210,9 @@ stubs.verify_stubbed_calls
183
210
  This library aims to support and is [tested against][travis] the following Ruby
184
211
  implementations:
185
212
 
186
- * MRI 1.8.7
187
- * MRI 1.9.2
188
- * MRI 1.9.3
189
- * MRI 2.0.0
190
- * MRI 2.1.0
191
- * [JRuby][]
192
- * [Rubinius][]
213
+ * Ruby 1.8.7+
214
+ * [JRuby][] 1.7+
215
+ * [Rubinius][] 2+
193
216
 
194
217
  If something doesn't work on one of these Ruby versions, it's a bug.
195
218
 
@@ -209,12 +232,14 @@ of a major release, support for that Ruby version may be dropped.
209
232
  Copyright (c) 2009-2013 [Rick Olson](mailto:technoweenie@gmail.com), Zack Hobson.
210
233
  See [LICENSE][] for details.
211
234
 
212
- [travis]: http://travis-ci.org/lostisland/faraday
213
- [excon]: https://github.com/geemus/excon#readme
214
- [typhoeus]: https://github.com/typhoeus/typhoeus#readme
215
- [patron]: http://toland.github.com/patron/
235
+ [net_http]: http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/Net/HTTP.html
236
+ [persistent]: https://github.com/drbrain/net-http-persistent
237
+ [travis]: http://travis-ci.org/lostisland/faraday
238
+ [excon]: https://github.com/geemus/excon#readme
239
+ [typhoeus]: https://github.com/typhoeus/typhoeus#readme
240
+ [patron]: http://toland.github.com/patron/
216
241
  [eventmachine]: https://github.com/igrigorik/em-http-request#readme
217
- [httpclient]: https://github.com/nahi/httpclient
218
- [jruby]: http://jruby.org/
219
- [rubinius]: http://rubini.us/
220
- [license]: LICENSE.md
242
+ [httpclient]: https://github.com/nahi/httpclient
243
+ [jruby]: http://jruby.org/
244
+ [rubinius]: http://rubini.us/
245
+ [license]: LICENSE.md
@@ -14,7 +14,7 @@ require 'forwardable'
14
14
  # conn.get '/'
15
15
  #
16
16
  module Faraday
17
- VERSION = "0.9.1"
17
+ VERSION = "0.9.2"
18
18
 
19
19
  class << self
20
20
  # Public: Gets or sets the root path that Faraday is being loaded from.
@@ -70,6 +70,14 @@ module Faraday
70
70
  else
71
71
  raise Error::ConnectionFailed, err
72
72
  end
73
+ rescue Errno::ETIMEDOUT => err
74
+ raise Error::TimeoutError, err
75
+ rescue RuntimeError => err
76
+ if err.message == "connection closed by server"
77
+ raise Error::ConnectionFailed, err
78
+ else
79
+ raise
80
+ end
73
81
  rescue => err
74
82
  if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
75
83
  raise Faraday::SSLError, err
@@ -41,6 +41,7 @@ module Faraday
41
41
  if req[:proxy]
42
42
  opts[:proxy] = {
43
43
  :host => req[:proxy][:uri].host,
44
+ :hostname => req[:proxy][:uri].hostname,
44
45
  :port => req[:proxy][:uri].port,
45
46
  :scheme => req[:proxy][:uri].scheme,
46
47
  :user => req[:proxy][:user],
@@ -10,6 +10,9 @@ module Faraday
10
10
  def call(env)
11
11
  super
12
12
 
13
+ # enable compression
14
+ client.transparent_gzip_decompression = true
15
+
13
16
  if req = env[:request]
14
17
  if proxy = req[:proxy]
15
18
  configure_proxy proxy
@@ -37,7 +40,7 @@ module Faraday
37
40
  save_response env, resp.status, resp.body, resp.headers
38
41
 
39
42
  @app.call env
40
- rescue ::HTTPClient::TimeoutError
43
+ rescue ::HTTPClient::TimeoutError, Errno::ETIMEDOUT
41
44
  raise Faraday::Error::TimeoutError, $!
42
45
  rescue ::HTTPClient::BadResponseError => err
43
46
  if err.message.include?('status 407')
@@ -69,14 +72,14 @@ module Faraday
69
72
 
70
73
  def configure_ssl(ssl)
71
74
  ssl_config = client.ssl_config
75
+ ssl_config.verify_mode = ssl_verify_mode(ssl)
76
+ ssl_config.cert_store = ssl_cert_store(ssl)
72
77
 
73
78
  ssl_config.add_trust_ca ssl[:ca_file] if ssl[:ca_file]
74
79
  ssl_config.add_trust_ca ssl[:ca_path] if ssl[:ca_path]
75
- ssl_config.cert_store = ssl[:cert_store] if ssl[:cert_store]
76
80
  ssl_config.client_cert = ssl[:client_cert] if ssl[:client_cert]
77
81
  ssl_config.client_key = ssl[:client_key] if ssl[:client_key]
78
82
  ssl_config.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
79
- ssl_config.verify_mode = ssl_verify_mode(ssl)
80
83
  end
81
84
 
82
85
  def configure_timeouts(req)
@@ -92,6 +95,14 @@ module Faraday
92
95
  end
93
96
  end
94
97
 
98
+ def ssl_cert_store(ssl)
99
+ return ssl[:cert_store] if ssl[:cert_store]
100
+ # Use the default cert store by default, i.e. system ca certs
101
+ cert_store = OpenSSL::X509::Store.new
102
+ cert_store.set_default_paths
103
+ cert_store
104
+ end
105
+
95
106
  def ssl_verify_mode(ssl)
96
107
  ssl[:verify_mode] || begin
97
108
  if ssl.fetch(:verify, true)
@@ -54,7 +54,7 @@ module Faraday
54
54
  end
55
55
 
56
56
  @app.call env
57
- rescue Timeout::Error => err
57
+ rescue Timeout::Error, Errno::ETIMEDOUT => err
58
58
  raise Faraday::Error::TimeoutError, err
59
59
  end
60
60
 
@@ -92,7 +92,7 @@ module Faraday
92
92
  Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
93
93
  else
94
94
  Net::HTTP
95
- end.new(env[:url].host, env[:url].port)
95
+ end.new(env[:url].host, env[:url].port || (env[:url].scheme == 'https' ? 443 : 80))
96
96
  end
97
97
 
98
98
  def configure_ssl(http, ssl)
@@ -4,7 +4,6 @@
4
4
 
5
5
  module Faraday
6
6
  class Adapter
7
- # Experimental adapter for net-http-persistent
8
7
  class NetHttpPersistent < NetHttp
9
8
  dependency 'net/http/persistent'
10
9
 
@@ -24,6 +23,8 @@ module Faraday
24
23
 
25
24
  def perform_request(http, env)
26
25
  http.request env[:url], create_request(env)
26
+ rescue Errno::ETIMEDOUT => error
27
+ raise Faraday::Error::TimeoutError, error
27
28
  rescue Net::HTTP::Persistent::Error => error
28
29
  if error.message.include? 'Timeout'
29
30
  raise Faraday::Error::TimeoutError, error
@@ -56,8 +56,14 @@ module Faraday
56
56
  # HAX: helps but doesn't work completely
57
57
  # https://github.com/toland/patron/issues/34
58
58
  ::Patron::Request::VALID_ACTIONS.tap do |actions|
59
- actions << :patch unless actions.include? :patch
60
- actions << :options unless actions.include? :options
59
+ if actions[0].is_a?(Symbol)
60
+ actions << :patch unless actions.include? :patch
61
+ actions << :options unless actions.include? :options
62
+ else
63
+ # Patron 0.4.20 and up
64
+ actions << "PATCH" unless actions.include? "PATCH"
65
+ actions << "OPTIONS" unless actions.include? "OPTIONS"
66
+ end
61
67
  end
62
68
  end
63
69
 
@@ -396,7 +396,7 @@ module Faraday
396
396
  # of the resulting url (default: nil).
397
397
  #
398
398
  # Returns the resulting URI instance.
399
- def build_exclusive_url(url = nil, params = nil)
399
+ def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
400
400
  url = nil if url.respond_to?(:empty?) and url.empty?
401
401
  base = url_prefix
402
402
  if url and base.path and base.path !~ /\/$/
@@ -404,7 +404,7 @@ module Faraday
404
404
  base.path = base.path + '/' # ensure trailing slash
405
405
  end
406
406
  uri = url ? base + url : base
407
- uri.query = params.to_query(options.params_encoder) if params
407
+ uri.query = params.to_query(params_encoder || options.params_encoder) if params
408
408
  uri.query = nil if uri.query and uri.query.empty?
409
409
  uri
410
410
  end
@@ -413,7 +413,12 @@ module Faraday
413
413
  #
414
414
  # Returns a Faraday::Connection.
415
415
  def dup
416
- self.class.new(build_exclusive_url, :headers => headers.dup, :params => params.dup, :builder => builder.dup, :ssl => ssl.dup)
416
+ self.class.new(build_exclusive_url,
417
+ :headers => headers.dup,
418
+ :params => params.dup,
419
+ :builder => builder.dup,
420
+ :ssl => ssl.dup,
421
+ :request => options.dup)
417
422
  end
418
423
 
419
424
  # Internal: Yields username and password extracted from a URI if they both exist.
@@ -269,6 +269,15 @@ module Faraday
269
269
 
270
270
  def_delegators :request, :params_encoder
271
271
 
272
+ # Public
273
+ def self.from(value)
274
+ env = super(value)
275
+ if value.respond_to?(:custom_members)
276
+ env.custom_members.update(value.custom_members)
277
+ end
278
+ env
279
+ end
280
+
272
281
  # Public
273
282
  def [](key)
274
283
  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
@@ -151,8 +151,9 @@ module Faraday
151
151
  lock!
152
152
  to_app(lambda { |env|
153
153
  response = Response.new
154
- response.finish(env) unless env.parallel?
155
154
  env.response = response
155
+ response.finish(env) unless env.parallel?
156
+ response
156
157
  })
157
158
  end
158
159
  end
@@ -188,7 +189,7 @@ module Faraday
188
189
  # :ssl - Hash of options for configuring SSL requests.
189
190
  def build_env(connection, request)
190
191
  Env.new(request.method, request.body,
191
- connection.build_exclusive_url(request.path, request.params),
192
+ connection.build_exclusive_url(request.path, request.params, request.options.params_encoder),
192
193
  request.options, request.headers, connection.ssl,
193
194
  connection.parallel_manager)
194
195
  end
@@ -22,8 +22,8 @@ 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)
27
27
  DEFAULT_CHECK = lambda { |env,exception| false }
28
28
 
29
29
  def self.from(value)
@@ -42,8 +42,12 @@ module Faraday
42
42
  (self[:interval] ||= 0).to_f
43
43
  end
44
44
 
45
+ def max_interval
46
+ (self[:max_interval] ||= Float::MAX).to_f
47
+ end
48
+
45
49
  def interval_randomness
46
- (self[:interval_randomness] ||= 0).to_i
50
+ (self[:interval_randomness] ||= 0).to_f
47
51
  end
48
52
 
49
53
  def backoff_factor
@@ -73,6 +77,7 @@ module Faraday
73
77
  # interval_randomness - The maximum random interval amount expressed
74
78
  # as a float between 0 and 1 to use in addition to the
75
79
  # interval. (default: 0)
80
+ # max_interval - An upper limit for the interval (default: Float::MAX)
76
81
  # backoff_factor - The amount to multiple each successive retry's
77
82
  # interval amount by in order to provide backoff
78
83
  # (default: 1)
@@ -98,6 +103,7 @@ module Faraday
98
103
  def sleep_amount(retries)
99
104
  retry_index = @options.max - retries
100
105
  current_interval = @options.interval * (@options.backoff_factor ** retry_index)
106
+ current_interval = [current_interval, @options.max_interval].min
101
107
  random_interval = rand * @options.interval_randomness.to_f * @options.interval
102
108
  current_interval + random_interval
103
109
  end
@@ -61,8 +61,8 @@ module Faraday
61
61
 
62
62
  def finish(env)
63
63
  raise "response already finished" if finished?
64
- @on_complete_callbacks.each { |callback| callback.call(env) }
65
- @env = Env.from(env)
64
+ @env = env.is_a?(Env) ? env : Env.from(env)
65
+ @on_complete_callbacks.each { |callback| callback.call(@env) }
66
66
  return self
67
67
  end
68
68