faraday 0.6.0 → 0.9.0

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 (83) hide show
  1. checksums.yaml +7 -0
  2. data/.document +6 -0
  3. data/CHANGELOG.md +15 -0
  4. data/CONTRIBUTING.md +36 -0
  5. data/Gemfile +25 -15
  6. data/{LICENSE → LICENSE.md} +1 -1
  7. data/README.md +187 -137
  8. data/Rakefile +38 -101
  9. data/faraday.gemspec +26 -81
  10. data/lib/faraday/adapter/em_http.rb +237 -0
  11. data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
  12. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
  13. data/lib/faraday/adapter/em_synchrony.rb +65 -38
  14. data/lib/faraday/adapter/excon.rb +60 -9
  15. data/lib/faraday/adapter/httpclient.rb +106 -0
  16. data/lib/faraday/adapter/net_http.rb +89 -38
  17. data/lib/faraday/adapter/net_http_persistent.rb +47 -0
  18. data/lib/faraday/adapter/patron.rb +46 -9
  19. data/lib/faraday/adapter/rack.rb +58 -0
  20. data/lib/faraday/adapter/test.rb +68 -28
  21. data/lib/faraday/adapter/typhoeus.rb +94 -16
  22. data/lib/faraday/adapter.rb +33 -25
  23. data/lib/faraday/autoload.rb +85 -0
  24. data/lib/faraday/connection.rb +342 -134
  25. data/lib/faraday/error.rb +44 -29
  26. data/lib/faraday/middleware.rb +18 -10
  27. data/lib/faraday/options.rb +350 -0
  28. data/lib/faraday/parameters.rb +193 -0
  29. data/lib/faraday/rack_builder.rb +212 -0
  30. data/lib/faraday/request/authorization.rb +42 -0
  31. data/lib/faraday/request/basic_authentication.rb +13 -0
  32. data/lib/faraday/request/instrumentation.rb +36 -0
  33. data/lib/faraday/request/multipart.rb +15 -15
  34. data/lib/faraday/request/retry.rb +118 -0
  35. data/lib/faraday/request/token_authentication.rb +15 -0
  36. data/lib/faraday/request/url_encoded.rb +8 -9
  37. data/lib/faraday/request.rb +46 -45
  38. data/lib/faraday/response/logger.rb +4 -4
  39. data/lib/faraday/response/raise_error.rb +8 -3
  40. data/lib/faraday/response.rb +20 -25
  41. data/lib/faraday/upload_io.rb +51 -7
  42. data/lib/faraday/utils.rb +240 -44
  43. data/lib/faraday.rb +218 -38
  44. data/script/console +7 -0
  45. data/script/generate_certs +42 -0
  46. data/script/package +7 -0
  47. data/script/proxy-server +42 -0
  48. data/script/release +17 -0
  49. data/script/server +36 -0
  50. data/script/test +172 -0
  51. data/test/adapters/default_test.rb +14 -0
  52. data/test/adapters/em_http_test.rb +20 -0
  53. data/test/adapters/em_synchrony_test.rb +20 -0
  54. data/test/adapters/excon_test.rb +20 -0
  55. data/test/adapters/httpclient_test.rb +21 -0
  56. data/test/adapters/integration.rb +254 -0
  57. data/test/adapters/logger_test.rb +3 -3
  58. data/test/adapters/net_http_persistent_test.rb +20 -0
  59. data/test/adapters/net_http_test.rb +6 -25
  60. data/test/adapters/patron_test.rb +20 -0
  61. data/test/adapters/rack_test.rb +31 -0
  62. data/test/adapters/test_middleware_test.rb +74 -3
  63. data/test/adapters/typhoeus_test.rb +28 -0
  64. data/test/authentication_middleware_test.rb +65 -0
  65. data/test/composite_read_io_test.rb +111 -0
  66. data/test/connection_test.rb +381 -104
  67. data/test/env_test.rb +116 -40
  68. data/test/helper.rb +61 -25
  69. data/test/live_server.rb +55 -29
  70. data/test/middleware/instrumentation_test.rb +88 -0
  71. data/test/middleware/retry_test.rb +109 -0
  72. data/test/middleware_stack_test.rb +87 -5
  73. data/test/multibyte.txt +1 -0
  74. data/test/options_test.rb +252 -0
  75. data/test/request_middleware_test.rb +78 -21
  76. data/test/response_middleware_test.rb +32 -7
  77. data/test/strawberry.rb +2 -0
  78. data/test/utils_test.rb +58 -0
  79. metadata +116 -117
  80. data/lib/faraday/adapter/action_dispatch.rb +0 -30
  81. data/lib/faraday/builder.rb +0 -137
  82. data/lib/faraday/request/json.rb +0 -31
  83. data/test/adapters/live_test.rb +0 -186
@@ -1,16 +1,14 @@
1
1
  module Faraday
2
2
  class Middleware
3
- class << self
4
- attr_accessor :load_error, :supports_parallel_requests
5
- alias supports_parallel_requests? supports_parallel_requests
3
+ extend MiddlewareRegistry
6
4
 
7
- # valid parallel managers should respond to #run with no parameters.
8
- # otherwise, return a short wrapper around it.
9
- def setup_parallel_manager(options = {})
10
- nil
11
- end
5
+ class << self
6
+ attr_accessor :load_error
7
+ private :load_error=
12
8
  end
13
9
 
10
+ self.load_error = nil
11
+
14
12
  # Executes a block which should try to require and reference dependent libraries
15
13
  def self.dependency(lib = nil)
16
14
  lib ? require(lib) : yield
@@ -18,12 +16,22 @@ module Faraday
18
16
  self.load_error = error
19
17
  end
20
18
 
19
+ def self.new(*)
20
+ raise "missing dependency for #{self}: #{load_error.message}" unless loaded?
21
+ super
22
+ end
23
+
21
24
  def self.loaded?
22
- @load_error.nil?
25
+ load_error.nil?
26
+ end
27
+
28
+ def self.inherited(subclass)
29
+ super
30
+ subclass.send(:load_error=, self.load_error)
23
31
  end
24
32
 
25
33
  def initialize(app = nil)
26
34
  @app = app
27
35
  end
28
36
  end
29
- end
37
+ end
@@ -0,0 +1,350 @@
1
+ module Faraday
2
+ # Subclasses Struct with some special helpers for converting from a Hash to
3
+ # a Struct.
4
+ class Options < Struct
5
+ # Public
6
+ def self.from(value)
7
+ value ? new.update(value) : new
8
+ end
9
+
10
+ # Public
11
+ def each
12
+ return to_enum(:each) unless block_given?
13
+ members.each do |key|
14
+ yield(key.to_sym, send(key))
15
+ end
16
+ end
17
+
18
+ # Public
19
+ def update(obj)
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
29
+ end
30
+
31
+ self.send("#{key}=", value) unless value.nil?
32
+ end
33
+ self
34
+ end
35
+
36
+ alias merge! update
37
+
38
+ # Public
39
+ def delete(key)
40
+ value = send(key)
41
+ send("#{key}=", nil)
42
+ value
43
+ end
44
+
45
+ # Public
46
+ def clear
47
+ members.each { |member| delete(member) }
48
+ end
49
+
50
+ # Public
51
+ def merge(value)
52
+ dup.update(value)
53
+ end
54
+
55
+ # Public
56
+ def fetch(key, *args)
57
+ unless symbolized_key_set.include?(key.to_sym)
58
+ key_setter = "#{key}="
59
+ if args.size > 0
60
+ send(key_setter, args.first)
61
+ elsif block_given?
62
+ send(key_setter, Proc.new.call(key))
63
+ else
64
+ raise self.class.fetch_error_class, "key not found: #{key.inspect}"
65
+ end
66
+ end
67
+ send(key)
68
+ end
69
+
70
+ # Public
71
+ def values_at(*keys)
72
+ keys.map { |key| send(key) }
73
+ end
74
+
75
+ # Public
76
+ def keys
77
+ members.reject { |member| send(member).nil? }
78
+ end
79
+
80
+ # Public
81
+ def empty?
82
+ keys.empty?
83
+ end
84
+
85
+ # Public
86
+ def each_key
87
+ return to_enum(:each_key) unless block_given?
88
+ keys.each do |key|
89
+ yield(key)
90
+ end
91
+ end
92
+
93
+ # Public
94
+ def key?(key)
95
+ keys.include?(key)
96
+ end
97
+
98
+ alias has_key? key?
99
+
100
+ # Public
101
+ def each_value
102
+ return to_enum(:each_value) unless block_given?
103
+ values.each do |value|
104
+ yield(value)
105
+ end
106
+ end
107
+
108
+ # Public
109
+ def value?(value)
110
+ values.include?(value)
111
+ end
112
+
113
+ alias has_value? value?
114
+
115
+ # Public
116
+ def to_hash
117
+ hash = {}
118
+ members.each do |key|
119
+ value = send(key)
120
+ hash[key.to_sym] = value unless value.nil?
121
+ end
122
+ hash
123
+ end
124
+
125
+ # Internal
126
+ def inspect
127
+ values = []
128
+ members.each do |member|
129
+ value = send(member)
130
+ values << "#{member}=#{value.inspect}" if value
131
+ end
132
+ values = values.empty? ? ' (empty)' : (' ' << values.join(", "))
133
+
134
+ %(#<#{self.class}#{values}>)
135
+ end
136
+
137
+ # Internal
138
+ def self.options(mapping)
139
+ attribute_options.update(mapping)
140
+ end
141
+
142
+ # Internal
143
+ def self.options_for(key)
144
+ attribute_options[key]
145
+ end
146
+
147
+ # Internal
148
+ def self.attribute_options
149
+ @attribute_options ||= {}
150
+ end
151
+
152
+ def self.memoized(key)
153
+ memoized_attributes[key.to_sym] = Proc.new
154
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
155
+ def #{key}() self[:#{key}]; end
156
+ RUBY
157
+ end
158
+
159
+ def self.memoized_attributes
160
+ @memoized_attributes ||= {}
161
+ end
162
+
163
+ def [](key)
164
+ key = key.to_sym
165
+ if method = self.class.memoized_attributes[key]
166
+ super(key) || (self[key] = instance_eval(&method))
167
+ else
168
+ super
169
+ end
170
+ end
171
+
172
+ def symbolized_key_set
173
+ @symbolized_key_set ||= Set.new(keys.map { |k| k.to_sym })
174
+ end
175
+
176
+ def self.inherited(subclass)
177
+ super
178
+ subclass.attribute_options.update(attribute_options)
179
+ subclass.memoized_attributes.update(memoized_attributes)
180
+ end
181
+
182
+ def self.fetch_error_class
183
+ @fetch_error_class ||= if Object.const_defined?(:KeyError)
184
+ ::KeyError
185
+ else
186
+ ::IndexError
187
+ end
188
+ end
189
+ end
190
+
191
+ class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
192
+ :timeout, :open_timeout, :boundary,
193
+ :oauth)
194
+
195
+ def []=(key, value)
196
+ if key && key.to_sym == :proxy
197
+ super(key, value ? ProxyOptions.from(value) : nil)
198
+ else
199
+ super(key, value)
200
+ end
201
+ end
202
+ end
203
+
204
+ class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
205
+ :cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth, :version)
206
+
207
+ def verify?
208
+ verify != false
209
+ end
210
+
211
+ def disable?
212
+ !verify?
213
+ end
214
+ end
215
+
216
+ class ProxyOptions < Options.new(:uri, :user, :password)
217
+ extend Forwardable
218
+ def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, :path, :path=
219
+
220
+ def self.from(value)
221
+ case value
222
+ when String
223
+ value = {:uri => Utils.URI(value)}
224
+ when URI
225
+ value = {:uri => value}
226
+ when Hash, Options
227
+ if uri = value.delete(:uri)
228
+ value[:uri] = Utils.URI(uri)
229
+ end
230
+ end
231
+ super(value)
232
+ end
233
+
234
+ memoized(:user) { uri.user && Utils.unescape(uri.user) }
235
+ memoized(:password) { uri.password && Utils.unescape(uri.password) }
236
+ end
237
+
238
+ class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
239
+ :parallel_manager, :params, :headers, :builder_class)
240
+
241
+ options :request => RequestOptions, :ssl => SSLOptions
242
+
243
+ memoized(:request) { self.class.options_for(:request).new }
244
+
245
+ memoized(:ssl) { self.class.options_for(:ssl).new }
246
+
247
+ memoized(:builder_class) { RackBuilder }
248
+
249
+ def new_builder(block)
250
+ builder_class.new(&block)
251
+ end
252
+ end
253
+
254
+ class Env < Options.new(:method, :body, :url, :request, :request_headers,
255
+ :ssl, :parallel_manager, :params, :response, :response_headers, :status)
256
+
257
+ ContentLength = 'Content-Length'.freeze
258
+ StatusesWithoutBody = Set.new [204, 304]
259
+ SuccessfulStatuses = 200..299
260
+
261
+ # A Set of HTTP verbs that typically send a body. If no body is set for
262
+ # these requests, the Content-Length header is set to 0.
263
+ MethodsWithBodies = Set.new [:post, :put, :patch, :options]
264
+
265
+ options :request => RequestOptions,
266
+ :request_headers => Utils::Headers, :response_headers => Utils::Headers
267
+
268
+ extend Forwardable
269
+
270
+ def_delegators :request, :params_encoder
271
+
272
+ # Public
273
+ def [](key)
274
+ if in_member_set?(key)
275
+ super(key)
276
+ else
277
+ custom_members[key]
278
+ end
279
+ end
280
+
281
+ # Public
282
+ def []=(key, value)
283
+ if in_member_set?(key)
284
+ super(key, value)
285
+ else
286
+ custom_members[key] = value
287
+ end
288
+ end
289
+
290
+ # Public
291
+ def success?
292
+ SuccessfulStatuses.include?(status)
293
+ end
294
+
295
+ # Public
296
+ def needs_body?
297
+ !body && MethodsWithBodies.include?(method)
298
+ end
299
+
300
+ # Public
301
+ def clear_body
302
+ request_headers[ContentLength] = '0'
303
+ self.body = ''
304
+ end
305
+
306
+ # Public
307
+ def parse_body?
308
+ !StatusesWithoutBody.include?(status)
309
+ end
310
+
311
+ # Public
312
+ def parallel?
313
+ !!parallel_manager
314
+ end
315
+
316
+ def inspect
317
+ attrs = [nil]
318
+ members.each do |mem|
319
+ if value = send(mem)
320
+ attrs << "@#{mem}=#{value.inspect}"
321
+ end
322
+ end
323
+ if !custom_members.empty?
324
+ attrs << "@custom=#{custom_members.inspect}"
325
+ end
326
+ %(#<#{self.class}#{attrs.join(" ")}>)
327
+ end
328
+
329
+ # Internal
330
+ def custom_members
331
+ @custom_members ||= {}
332
+ end
333
+
334
+ # Internal
335
+ if members.first.is_a?(Symbol)
336
+ def in_member_set?(key)
337
+ self.class.member_set.include?(key.to_sym)
338
+ end
339
+ else
340
+ def in_member_set?(key)
341
+ self.class.member_set.include?(key.to_s)
342
+ end
343
+ end
344
+
345
+ # Internal
346
+ def self.member_set
347
+ @member_set ||= Set.new(members)
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,193 @@
1
+ module Faraday
2
+ module NestedParamsEncoder
3
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
4
+
5
+ def self.escape(s)
6
+ return s.to_s.gsub(ESCAPE_RE) {
7
+ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
8
+ }.tr(' ', '+')
9
+ end
10
+
11
+ def self.unescape(s)
12
+ CGI.unescape(s.to_s)
13
+ end
14
+
15
+ def self.encode(params)
16
+ return nil if params == nil
17
+
18
+ if !params.is_a?(Array)
19
+ if !params.respond_to?(:to_hash)
20
+ raise TypeError,
21
+ "Can't convert #{params.class} into Hash."
22
+ end
23
+ params = params.to_hash
24
+ params = params.map do |key, value|
25
+ key = key.to_s if key.kind_of?(Symbol)
26
+ [key, value]
27
+ end
28
+ # Useful default for OAuth and caching.
29
+ # Only to be used for non-Array inputs. Arrays should preserve order.
30
+ params.sort!
31
+ end
32
+
33
+ # Helper lambda
34
+ to_query = lambda do |parent, value|
35
+ if value.is_a?(Hash)
36
+ value = value.map do |key, val|
37
+ key = escape(key)
38
+ [key, val]
39
+ end
40
+ value.sort!
41
+ buffer = ""
42
+ value.each do |key, val|
43
+ new_parent = "#{parent}%5B#{key}%5D"
44
+ buffer << "#{to_query.call(new_parent, val)}&"
45
+ end
46
+ return buffer.chop
47
+ elsif value.is_a?(Array)
48
+ buffer = ""
49
+ value.each_with_index do |val, i|
50
+ new_parent = "#{parent}%5B%5D"
51
+ buffer << "#{to_query.call(new_parent, val)}&"
52
+ end
53
+ return buffer.chop
54
+ else
55
+ encoded_value = escape(value)
56
+ return "#{parent}=#{encoded_value}"
57
+ end
58
+ end
59
+
60
+ # The params have form [['key1', 'value1'], ['key2', 'value2']].
61
+ buffer = ''
62
+ params.each do |parent, value|
63
+ encoded_parent = escape(parent)
64
+ buffer << "#{to_query.call(encoded_parent, value)}&"
65
+ end
66
+ return buffer.chop
67
+ end
68
+
69
+ def self.decode(query)
70
+ return nil if query == nil
71
+ # Recursive helper lambda
72
+ dehash = lambda do |hash|
73
+ hash.each do |(key, value)|
74
+ if value.kind_of?(Hash)
75
+ hash[key] = dehash.call(value)
76
+ end
77
+ end
78
+ # Numeric keys implies an array
79
+ if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
80
+ hash.sort.inject([]) do |accu, (_, value)|
81
+ accu << value; accu
82
+ end
83
+ else
84
+ hash
85
+ end
86
+ end
87
+
88
+ empty_accumulator = {}
89
+ return ((query.split('&').map do |pair|
90
+ pair.split('=', 2) if pair && !pair.empty?
91
+ end).compact.inject(empty_accumulator.dup) do |accu, (key, value)|
92
+ key = unescape(key)
93
+ if value.kind_of?(String)
94
+ value = unescape(value.gsub(/\+/, ' '))
95
+ end
96
+
97
+ array_notation = !!(key =~ /\[\]$/)
98
+ subkeys = key.split(/[\[\]]+/)
99
+ current_hash = accu
100
+ for i in 0...(subkeys.size - 1)
101
+ subkey = subkeys[i]
102
+ current_hash[subkey] = {} unless current_hash[subkey]
103
+ current_hash = current_hash[subkey]
104
+ end
105
+ if array_notation
106
+ current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
107
+ current_hash[subkeys.last] << value
108
+ else
109
+ current_hash[subkeys.last] = value
110
+ end
111
+ accu
112
+ end).inject(empty_accumulator.dup) do |accu, (key, value)|
113
+ accu[key] = value.kind_of?(Hash) ? dehash.call(value) : value
114
+ accu
115
+ end
116
+ end
117
+ end
118
+
119
+ module FlatParamsEncoder
120
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
121
+
122
+ def self.escape(s)
123
+ return s.to_s.gsub(ESCAPE_RE) {
124
+ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
125
+ }.tr(' ', '+')
126
+ end
127
+
128
+ def self.unescape(s)
129
+ CGI.unescape(s.to_s)
130
+ end
131
+
132
+ def self.encode(params)
133
+ return nil if params == nil
134
+
135
+ if !params.is_a?(Array)
136
+ if !params.respond_to?(:to_hash)
137
+ raise TypeError,
138
+ "Can't convert #{params.class} into Hash."
139
+ end
140
+ params = params.to_hash
141
+ params = params.map do |key, value|
142
+ key = key.to_s if key.kind_of?(Symbol)
143
+ [key, value]
144
+ end
145
+ # Useful default for OAuth and caching.
146
+ # Only to be used for non-Array inputs. Arrays should preserve order.
147
+ params.sort!
148
+ end
149
+
150
+ # The params have form [['key1', 'value1'], ['key2', 'value2']].
151
+ buffer = ''
152
+ params.each do |key, value|
153
+ encoded_key = escape(key)
154
+ value = value.to_s if value == true || value == false
155
+ if value == nil
156
+ buffer << "#{encoded_key}&"
157
+ elsif value.kind_of?(Array)
158
+ value.each do |sub_value|
159
+ encoded_value = escape(sub_value)
160
+ buffer << "#{encoded_key}=#{encoded_value}&"
161
+ end
162
+ else
163
+ encoded_value = escape(value)
164
+ buffer << "#{encoded_key}=#{encoded_value}&"
165
+ end
166
+ end
167
+ return buffer.chop
168
+ end
169
+
170
+ def self.decode(query)
171
+ empty_accumulator = {}
172
+ return nil if query == nil
173
+ split_query = (query.split('&').map do |pair|
174
+ pair.split('=', 2) if pair && !pair.empty?
175
+ end).compact
176
+ return split_query.inject(empty_accumulator.dup) do |accu, pair|
177
+ pair[0] = unescape(pair[0])
178
+ pair[1] = true if pair[1].nil?
179
+ if pair[1].respond_to?(:to_str)
180
+ pair[1] = unescape(pair[1].to_str.gsub(/\+/, " "))
181
+ end
182
+ if accu[pair[0]].kind_of?(Array)
183
+ accu[pair[0]] << pair[1]
184
+ elsif accu[pair[0]]
185
+ accu[pair[0]] = [accu[pair[0]], pair[1]]
186
+ else
187
+ accu[pair[0]] = pair[1]
188
+ end
189
+ accu
190
+ end
191
+ end
192
+ end
193
+ end