faraday 0.9.1 → 0.9.2

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 (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