faraday 0.9.0.rc5 → 0.9.0.rc6
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.
- data/Gemfile +6 -5
- data/README.md +0 -22
- data/faraday.gemspec +2 -2
- data/lib/faraday.rb +6 -2
- data/lib/faraday/adapter/em_http.rb +34 -14
- data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
- data/lib/faraday/adapter/em_synchrony.rb +21 -20
- data/lib/faraday/adapter/excon.rb +16 -0
- data/lib/faraday/adapter/httpclient.rb +14 -0
- data/lib/faraday/adapter/net_http.rb +6 -2
- data/lib/faraday/adapter/net_http_persistent.rb +15 -3
- data/lib/faraday/adapter/patron.rb +13 -11
- data/lib/faraday/adapter/typhoeus.rb +11 -0
- data/lib/faraday/autoload.rb +2 -4
- data/lib/faraday/connection.rb +30 -3
- data/lib/faraday/error.rb +4 -1
- data/lib/faraday/options.rb +117 -23
- data/lib/faraday/request/instrumentation.rb +1 -3
- data/lib/faraday/request/multipart.rb +1 -1
- data/lib/faraday/request/retry.rb +38 -9
- data/lib/faraday/response.rb +1 -2
- data/lib/faraday/response/raise_error.rb +3 -0
- data/lib/faraday/utils.rb +10 -4
- data/script/proxy-server +42 -0
- data/script/server +1 -3
- data/script/test +35 -7
- data/test/adapters/excon_test.rb +4 -0
- data/test/adapters/httpclient_test.rb +5 -0
- data/test/adapters/integration.rb +48 -2
- data/test/adapters/net_http_persistent_test.rb +10 -1
- data/test/adapters/patron_test.rb +3 -0
- data/test/adapters/rack_test.rb +5 -0
- data/test/adapters/typhoeus_test.rb +3 -13
- data/test/authentication_middleware_test.rb +6 -6
- data/test/connection_test.rb +123 -49
- data/test/env_test.rb +19 -1
- data/test/helper.rb +2 -4
- data/test/live_server.rb +4 -2
- data/test/middleware/instrumentation_test.rb +13 -0
- data/test/middleware/retry_test.rb +47 -0
- data/test/multibyte.txt +1 -0
- data/test/options_test.rb +93 -17
- data/test/request_middleware_test.rb +6 -6
- data/test/utils_test.rb +34 -13
- metadata +69 -44
@@ -56,6 +56,17 @@ module Faraday
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
case resp.curl_return_code
|
60
|
+
when 0
|
61
|
+
# everything OK
|
62
|
+
when 7
|
63
|
+
raise Error::ConnectionFailed, resp.curl_error_message
|
64
|
+
when 60
|
65
|
+
raise Faraday::SSLError, resp.curl_error_message
|
66
|
+
else
|
67
|
+
raise Error::ClientError, resp.curl_error_message
|
68
|
+
end
|
69
|
+
|
59
70
|
save_response(env, resp.code, resp.body) do |response_headers|
|
60
71
|
response_headers.parse resp.headers
|
61
72
|
end
|
data/lib/faraday/autoload.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'faraday'
|
2
|
-
|
3
1
|
module Faraday
|
4
2
|
# Internal: Adds the ability for other modules to manage autoloadable
|
5
3
|
# constants.
|
@@ -74,7 +72,8 @@ module Faraday
|
|
74
72
|
:Timeout => 'timeout',
|
75
73
|
:Authorization => 'authorization',
|
76
74
|
:BasicAuthentication => 'basic_authentication',
|
77
|
-
:TokenAuthentication => 'token_authentication'
|
75
|
+
:TokenAuthentication => 'token_authentication',
|
76
|
+
:Instrumentation => 'instrumentation'
|
78
77
|
end
|
79
78
|
|
80
79
|
class Response
|
@@ -84,4 +83,3 @@ module Faraday
|
|
84
83
|
:Logger => 'logger'
|
85
84
|
end
|
86
85
|
end
|
87
|
-
|
data/lib/faraday/connection.rb
CHANGED
@@ -79,7 +79,14 @@ module Faraday
|
|
79
79
|
@params.update(options.params) if options.params
|
80
80
|
@headers.update(options.headers) if options.headers
|
81
81
|
|
82
|
-
proxy
|
82
|
+
@proxy = nil
|
83
|
+
proxy(options.fetch(:proxy) {
|
84
|
+
uri = ENV['http_proxy']
|
85
|
+
if uri && !uri.empty?
|
86
|
+
uri = 'http://' + uri if uri !~ /^http/i
|
87
|
+
uri
|
88
|
+
end
|
89
|
+
})
|
83
90
|
|
84
91
|
yield self if block_given?
|
85
92
|
|
@@ -312,7 +319,7 @@ module Faraday
|
|
312
319
|
end
|
313
320
|
|
314
321
|
# Public: Sets the path prefix and ensures that it always has a leading
|
315
|
-
#
|
322
|
+
# slash.
|
316
323
|
#
|
317
324
|
# value - A String.
|
318
325
|
#
|
@@ -324,6 +331,27 @@ module Faraday
|
|
324
331
|
end
|
325
332
|
end
|
326
333
|
|
334
|
+
# Public: Takes a relative url for a request and combines it with the defaults
|
335
|
+
# set on the connection instance.
|
336
|
+
#
|
337
|
+
# conn = Faraday::Connection.new { ... }
|
338
|
+
# conn.url_prefix = "https://sushi.com/api?token=abc"
|
339
|
+
# conn.scheme # => https
|
340
|
+
# conn.path_prefix # => "/api"
|
341
|
+
#
|
342
|
+
# conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2
|
343
|
+
# conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2
|
344
|
+
#
|
345
|
+
def build_url(url = nil, extra_params = nil)
|
346
|
+
uri = build_exclusive_url(url)
|
347
|
+
|
348
|
+
query_values = params.dup.merge_query(uri.query, options.params_encoder)
|
349
|
+
query_values.update extra_params if extra_params
|
350
|
+
uri.query = query_values.empty? ? nil : query_values.to_query(options.params_encoder)
|
351
|
+
|
352
|
+
uri
|
353
|
+
end
|
354
|
+
|
327
355
|
# Builds and runs the Faraday::Request.
|
328
356
|
#
|
329
357
|
# method - The Symbol HTTP method.
|
@@ -400,4 +428,3 @@ module Faraday
|
|
400
428
|
end
|
401
429
|
end
|
402
430
|
end
|
403
|
-
|
data/lib/faraday/error.rb
CHANGED
@@ -38,8 +38,11 @@ module Faraday
|
|
38
38
|
class ParsingError < ClientError; end
|
39
39
|
class TimeoutError < ClientError; end
|
40
40
|
|
41
|
+
class SSLError < ClientError
|
42
|
+
end
|
43
|
+
|
41
44
|
[:MissingDependency, :ClientError, :ConnectionFailed, :ResourceNotFound,
|
42
|
-
:ParsingError, :TimeoutError].each do |const|
|
45
|
+
:ParsingError, :TimeoutError, :SSLError].each do |const|
|
43
46
|
Error.const_set(const, Faraday.const_get(const))
|
44
47
|
end
|
45
48
|
end
|
data/lib/faraday/options.rb
CHANGED
@@ -18,7 +18,7 @@ module Faraday
|
|
18
18
|
def update(obj)
|
19
19
|
obj.each do |key, value|
|
20
20
|
if sub_options = self.class.options_for(key)
|
21
|
-
value = sub_options.from(value)
|
21
|
+
value = sub_options.from(value) if value
|
22
22
|
elsif Hash === value
|
23
23
|
hash = {}
|
24
24
|
value.each do |hash_key, hash_value|
|
@@ -27,7 +27,7 @@ module Faraday
|
|
27
27
|
value = hash
|
28
28
|
end
|
29
29
|
|
30
|
-
self.send("#{key}=", value)
|
30
|
+
self.send("#{key}=", value) unless value.nil?
|
31
31
|
end
|
32
32
|
self
|
33
33
|
end
|
@@ -47,9 +47,20 @@ module Faraday
|
|
47
47
|
end
|
48
48
|
|
49
49
|
# Public
|
50
|
-
def fetch(key,
|
51
|
-
send(key)
|
52
|
-
|
50
|
+
def fetch(key, *args)
|
51
|
+
return send(key) if symbolized_key_set.include?(key.to_sym)
|
52
|
+
|
53
|
+
key_setter = "#{key}="
|
54
|
+
|
55
|
+
if args.size > 0
|
56
|
+
return send(key_setter, args.first)
|
57
|
+
end
|
58
|
+
|
59
|
+
if block_given?
|
60
|
+
return send(key_setter, Proc.new.call(key))
|
61
|
+
end
|
62
|
+
|
63
|
+
raise self.class.fetch_error_class, "key not found: #{key.inspect}"
|
53
64
|
end
|
54
65
|
|
55
66
|
# Public
|
@@ -98,6 +109,44 @@ module Faraday
|
|
98
109
|
def self.attribute_options
|
99
110
|
@attribute_options ||= {}
|
100
111
|
end
|
112
|
+
|
113
|
+
def self.memoized(key)
|
114
|
+
memoized_attributes[key.to_sym] = Proc.new
|
115
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
116
|
+
def #{key}() self[:#{key}]; end
|
117
|
+
RUBY
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.memoized_attributes
|
121
|
+
@memoized_attributes ||= {}
|
122
|
+
end
|
123
|
+
|
124
|
+
def [](key)
|
125
|
+
key = key.to_sym
|
126
|
+
if method = self.class.memoized_attributes[key]
|
127
|
+
super(key) || (self[key] = instance_eval(&method))
|
128
|
+
else
|
129
|
+
super
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def symbolized_key_set
|
134
|
+
@symbolized_key_set ||= Set.new(keys.map { |k| k.to_sym })
|
135
|
+
end
|
136
|
+
|
137
|
+
def self.inherited(subclass)
|
138
|
+
super
|
139
|
+
subclass.attribute_options.update(attribute_options)
|
140
|
+
subclass.memoized_attributes.update(memoized_attributes)
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.fetch_error_class
|
144
|
+
@fetch_error_class ||= if Object.const_defined?(:KeyError)
|
145
|
+
::KeyError
|
146
|
+
else
|
147
|
+
::IndexError
|
148
|
+
end
|
149
|
+
end
|
101
150
|
end
|
102
151
|
|
103
152
|
class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
|
@@ -114,7 +163,7 @@ module Faraday
|
|
114
163
|
end
|
115
164
|
|
116
165
|
class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
|
117
|
-
:cert_store, :client_cert, :client_key, :verify_depth, :version)
|
166
|
+
:cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth, :version)
|
118
167
|
|
119
168
|
def verify?
|
120
169
|
verify != false
|
@@ -141,13 +190,8 @@ module Faraday
|
|
141
190
|
super(value)
|
142
191
|
end
|
143
192
|
|
144
|
-
|
145
|
-
|
146
|
-
end
|
147
|
-
|
148
|
-
def password
|
149
|
-
self[:password] ||= Utils.unescape(uri.password)
|
150
|
-
end
|
193
|
+
memoized(:user) { uri.user && Utils.unescape(uri.user) }
|
194
|
+
memoized(:password) { uri.password && Utils.unescape(uri.password) }
|
151
195
|
end
|
152
196
|
|
153
197
|
class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
|
@@ -155,17 +199,11 @@ module Faraday
|
|
155
199
|
|
156
200
|
options :request => RequestOptions, :ssl => SSLOptions
|
157
201
|
|
158
|
-
|
159
|
-
self[:request] ||= self.class.options_for(:request).new
|
160
|
-
end
|
202
|
+
memoized(:request) { self.class.options_for(:request).new }
|
161
203
|
|
162
|
-
|
163
|
-
self[:ssl] ||= self.class.options_for(:ssl).new
|
164
|
-
end
|
204
|
+
memoized(:ssl) { self.class.options_for(:ssl).new }
|
165
205
|
|
166
|
-
|
167
|
-
self[:builder_class] ||= RackBuilder
|
168
|
-
end
|
206
|
+
memoized(:builder_class) { RackBuilder }
|
169
207
|
|
170
208
|
def new_builder(block)
|
171
209
|
builder_class.new(&block)
|
@@ -190,26 +228,82 @@ module Faraday
|
|
190
228
|
|
191
229
|
def_delegators :request, :params_encoder
|
192
230
|
|
231
|
+
# Public
|
232
|
+
def [](key)
|
233
|
+
if in_member_set?(key)
|
234
|
+
super(key)
|
235
|
+
else
|
236
|
+
custom_members[key]
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# Public
|
241
|
+
def []=(key, value)
|
242
|
+
if in_member_set?(key)
|
243
|
+
super(key, value)
|
244
|
+
else
|
245
|
+
custom_members[key] = value
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Public
|
193
250
|
def success?
|
194
251
|
SuccessfulStatuses.include?(status)
|
195
252
|
end
|
196
253
|
|
254
|
+
# Public
|
197
255
|
def needs_body?
|
198
256
|
!body && MethodsWithBodies.include?(method)
|
199
257
|
end
|
200
258
|
|
259
|
+
# Public
|
201
260
|
def clear_body
|
202
261
|
request_headers[ContentLength] = '0'
|
203
262
|
self.body = ''
|
204
263
|
end
|
205
264
|
|
265
|
+
# Public
|
206
266
|
def parse_body?
|
207
267
|
!StatusesWithoutBody.include?(status)
|
208
268
|
end
|
209
269
|
|
270
|
+
# Public
|
210
271
|
def parallel?
|
211
272
|
!!parallel_manager
|
212
273
|
end
|
274
|
+
|
275
|
+
def inspect
|
276
|
+
attrs = [nil]
|
277
|
+
members.each do |mem|
|
278
|
+
if value = send(mem)
|
279
|
+
attrs << "@#{mem}=#{value.inspect}"
|
280
|
+
end
|
281
|
+
end
|
282
|
+
if !custom_members.empty?
|
283
|
+
attrs << "@custom=#{custom_members.inspect}"
|
284
|
+
end
|
285
|
+
%(#<#{self.class}#{attrs.join(" ")}>)
|
286
|
+
end
|
287
|
+
|
288
|
+
# Internal
|
289
|
+
def custom_members
|
290
|
+
@custom_members ||= {}
|
291
|
+
end
|
292
|
+
|
293
|
+
# Internal
|
294
|
+
if members.first.is_a?(Symbol)
|
295
|
+
def in_member_set?(key)
|
296
|
+
self.class.member_set.include?(key.to_sym)
|
297
|
+
end
|
298
|
+
else
|
299
|
+
def in_member_set?(key)
|
300
|
+
self.class.member_set.include?(key.to_s)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
# Internal
|
305
|
+
def self.member_set
|
306
|
+
@member_set ||= Set.new(members)
|
307
|
+
end
|
213
308
|
end
|
214
309
|
end
|
215
|
-
|
@@ -24,7 +24,7 @@ module Faraday
|
|
24
24
|
# end
|
25
25
|
def initialize(app, options = nil)
|
26
26
|
super(app)
|
27
|
-
@name, @instrumenter = Options.from(options).
|
27
|
+
@name, @instrumenter = Options.from(options).values_at(:name, :instrumenter)
|
28
28
|
end
|
29
29
|
|
30
30
|
def call(env)
|
@@ -34,5 +34,3 @@ module Faraday
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
-
|
38
|
-
|
@@ -8,7 +8,7 @@ module Faraday
|
|
8
8
|
def call(env)
|
9
9
|
match_content_type(env) do |params|
|
10
10
|
env.request.boundary ||= DEFAULT_BOUNDARY
|
11
|
-
env.request_headers[CONTENT_TYPE] += ";boundary=#{env.request.boundary}"
|
11
|
+
env.request_headers[CONTENT_TYPE] += "; boundary=#{env.request.boundary}"
|
12
12
|
env.body = create_multipart(env, params)
|
13
13
|
end
|
14
14
|
@app.call env
|
@@ -3,17 +3,23 @@ module Faraday
|
|
3
3
|
#
|
4
4
|
# By default, it retries 2 times and handles only timeout exceptions. It can
|
5
5
|
# be configured with an arbitrary number of retries, a list of exceptions to
|
6
|
-
# handle
|
6
|
+
# handle, a retry interval, a percentage of randomness to add to the retry
|
7
|
+
# interval, and a backoff factor.
|
7
8
|
#
|
8
9
|
# Examples
|
9
10
|
#
|
10
11
|
# Faraday.new do |conn|
|
11
12
|
# conn.request :retry, max: 2, interval: 0.05,
|
13
|
+
# interval_randomness: 0.5, backoff_factor: 2
|
12
14
|
# exceptions: [CustomException, 'Timeout::Error']
|
13
15
|
# conn.adapter ...
|
14
16
|
# end
|
17
|
+
#
|
18
|
+
# This example will result in a first interval that is random between 0.05 and 0.075 and a second
|
19
|
+
# interval that is random between 0.1 and 0.15
|
20
|
+
#
|
15
21
|
class Request::Retry < Faraday::Middleware
|
16
|
-
class Options < Faraday::Options.new(:max, :interval, :exceptions)
|
22
|
+
class Options < Faraday::Options.new(:max, :interval, :interval_randomness, :backoff_factor, :exceptions)
|
17
23
|
def self.from(value)
|
18
24
|
if Fixnum === value
|
19
25
|
new(value)
|
@@ -30,6 +36,14 @@ module Faraday
|
|
30
36
|
(self[:interval] ||= 0).to_f
|
31
37
|
end
|
32
38
|
|
39
|
+
def interval_randomness
|
40
|
+
(self[:interval_randomness] ||= 0).to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
def backoff_factor
|
44
|
+
(self[:backoff_factor] ||= 1).to_f
|
45
|
+
end
|
46
|
+
|
33
47
|
def exceptions
|
34
48
|
Array(self[:exceptions] ||= [Errno::ETIMEDOUT, 'Timeout::Error',
|
35
49
|
Error::TimeoutError])
|
@@ -40,25 +54,41 @@ module Faraday
|
|
40
54
|
# Public: Initialize middleware
|
41
55
|
#
|
42
56
|
# Options:
|
43
|
-
# max
|
44
|
-
# interval
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
57
|
+
# max - Maximum number of retries (default: 2)
|
58
|
+
# interval - Pause in seconds between retries (default: 0)
|
59
|
+
# interval_randomness - The maximum random interval amount expressed
|
60
|
+
# as a float between 0 and 1 to use in addition to the
|
61
|
+
# interval. (default: 0)
|
62
|
+
# backoff_factor - The amount to multiple each successive retry's
|
63
|
+
# interval amount by in order to provide backoff
|
64
|
+
# (default: 1)
|
65
|
+
# exceptions - The list of exceptions to handle. Exceptions can be
|
66
|
+
# given as Class, Module, or String. (default:
|
67
|
+
# [Errno::ETIMEDOUT, Timeout::Error,
|
68
|
+
# Error::TimeoutError])
|
48
69
|
def initialize(app, options = nil)
|
49
70
|
super(app)
|
50
71
|
@options = Options.from(options)
|
51
72
|
@errmatch = build_exception_matcher(@options.exceptions)
|
52
73
|
end
|
53
74
|
|
75
|
+
def sleep_amount(retries)
|
76
|
+
retry_index = @options.max - retries
|
77
|
+
current_interval = @options.interval * (@options.backoff_factor ** retry_index)
|
78
|
+
random_interval = rand * @options.interval_randomness.to_f * @options.interval
|
79
|
+
current_interval + random_interval
|
80
|
+
end
|
81
|
+
|
54
82
|
def call(env)
|
55
83
|
retries = @options.max
|
84
|
+
request_body = env[:body]
|
56
85
|
begin
|
86
|
+
env[:body] = request_body # after failure env[:body] is set to the response body
|
57
87
|
@app.call(env)
|
58
88
|
rescue @errmatch
|
59
89
|
if retries > 0
|
60
90
|
retries -= 1
|
61
|
-
sleep
|
91
|
+
sleep sleep_amount(retries + 1)
|
62
92
|
retry
|
63
93
|
end
|
64
94
|
raise
|
@@ -84,4 +114,3 @@ module Faraday
|
|
84
114
|
end
|
85
115
|
end
|
86
116
|
end
|
87
|
-
|
data/lib/faraday/response.rb
CHANGED
@@ -86,9 +86,8 @@ module Faraday
|
|
86
86
|
# Useful for applying request params after restoring a marshalled Response.
|
87
87
|
def apply_request(request_env)
|
88
88
|
raise "response didn't finish yet" unless finished?
|
89
|
-
@env = Env.from(request_env).
|
89
|
+
@env = Env.from(request_env).update(@env)
|
90
90
|
return self
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
94
|
-
|