faraday 0.9.1 → 0.15.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.
- checksums.yaml +5 -5
- data/LICENSE.md +1 -1
- data/README.md +189 -28
- data/lib/faraday/adapter/em_http.rb +8 -2
- data/lib/faraday/adapter/em_http_ssl_patch.rb +1 -1
- data/lib/faraday/adapter/em_synchrony.rb +16 -2
- data/lib/faraday/adapter/excon.rb +7 -8
- data/lib/faraday/adapter/httpclient.rb +27 -5
- data/lib/faraday/adapter/net_http.rb +16 -9
- data/lib/faraday/adapter/net_http_persistent.rb +24 -9
- data/lib/faraday/adapter/patron.rb +40 -12
- data/lib/faraday/adapter/test.rb +79 -28
- data/lib/faraday/adapter/typhoeus.rb +4 -115
- data/lib/faraday/adapter.rb +10 -1
- data/lib/faraday/autoload.rb +1 -1
- data/lib/faraday/connection.rb +72 -20
- data/lib/faraday/error.rb +18 -5
- data/lib/faraday/options.rb +40 -18
- data/lib/faraday/parameters.rb +54 -38
- data/lib/faraday/rack_builder.rb +27 -2
- data/lib/faraday/request/authorization.rb +1 -2
- data/lib/faraday/request/multipart.rb +7 -2
- data/lib/faraday/request/retry.rb +80 -17
- data/lib/faraday/request.rb +2 -0
- data/lib/faraday/response/logger.rb +28 -7
- data/lib/faraday/response.rb +6 -2
- data/lib/faraday/utils.rb +32 -3
- data/lib/faraday.rb +14 -34
- metadata +7 -93
- data/.document +0 -6
- data/CHANGELOG.md +0 -20
- data/CONTRIBUTING.md +0 -36
- data/Gemfile +0 -25
- data/Rakefile +0 -71
- data/faraday.gemspec +0 -34
- data/script/cached-bundle +0 -46
- data/script/console +0 -7
- data/script/generate_certs +0 -42
- data/script/package +0 -7
- data/script/proxy-server +0 -42
- data/script/release +0 -17
- data/script/s3-put +0 -71
- data/script/server +0 -36
- data/script/test +0 -172
- data/test/adapters/default_test.rb +0 -14
- data/test/adapters/em_http_test.rb +0 -20
- data/test/adapters/em_synchrony_test.rb +0 -20
- data/test/adapters/excon_test.rb +0 -20
- data/test/adapters/httpclient_test.rb +0 -21
- data/test/adapters/integration.rb +0 -254
- data/test/adapters/logger_test.rb +0 -82
- data/test/adapters/net_http_persistent_test.rb +0 -20
- data/test/adapters/net_http_test.rb +0 -14
- data/test/adapters/patron_test.rb +0 -20
- data/test/adapters/rack_test.rb +0 -31
- data/test/adapters/test_middleware_test.rb +0 -114
- data/test/adapters/typhoeus_test.rb +0 -28
- data/test/authentication_middleware_test.rb +0 -65
- data/test/composite_read_io_test.rb +0 -111
- data/test/connection_test.rb +0 -522
- data/test/env_test.rb +0 -218
- data/test/helper.rb +0 -81
- data/test/live_server.rb +0 -67
- data/test/middleware/instrumentation_test.rb +0 -88
- data/test/middleware/retry_test.rb +0 -177
- data/test/middleware_stack_test.rb +0 -173
- data/test/multibyte.txt +0 -1
- data/test/options_test.rb +0 -252
- data/test/parameters_test.rb +0 -64
- data/test/request_middleware_test.rb +0 -142
- data/test/response_middleware_test.rb +0 -72
- data/test/strawberry.rb +0 -2
- data/test/utils_test.rb +0 -58
data/lib/faraday/options.rb
CHANGED
@@ -18,23 +18,20 @@ module Faraday
|
|
18
18
|
# Public
|
19
19
|
def update(obj)
|
20
20
|
obj.each do |key, value|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
value.
|
26
|
-
|
27
|
-
|
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}=",
|
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(
|
52
|
-
|
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)
|
data/lib/faraday/parameters.rb
CHANGED
@@ -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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
84
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
data/lib/faraday/rack_builder.rb
CHANGED
@@ -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
|
@@ -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
|
-
|
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 ||=
|
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, :
|
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
|
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).
|
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
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
115
|
-
|
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
|
data/lib/faraday/request.rb
CHANGED
@@ -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('
|
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
|
data/lib/faraday/response.rb
CHANGED
@@ -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
|
-
@
|
65
|
-
@
|
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
|
-
|
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
|
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
|