faraday 0.14.0 → 0.17.6
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 +4 -4
- data/CHANGELOG.md +232 -0
- data/README.md +21 -7
- data/Rakefile +13 -0
- data/lib/faraday/adapter/em_http.rb +9 -9
- data/lib/faraday/adapter/em_synchrony.rb +5 -5
- data/lib/faraday/adapter/excon.rb +6 -3
- data/lib/faraday/adapter/httpclient.rb +4 -4
- data/lib/faraday/adapter/net_http.rb +25 -7
- data/lib/faraday/adapter/net_http_persistent.rb +33 -19
- data/lib/faraday/adapter/patron.rb +7 -12
- data/lib/faraday/adapter/rack.rb +1 -1
- data/lib/faraday/adapter.rb +2 -0
- data/lib/faraday/deprecate.rb +109 -0
- data/lib/faraday/error.rb +129 -34
- data/lib/faraday/options.rb +6 -5
- data/lib/faraday/parameters.rb +2 -1
- data/lib/faraday/rack_builder.rb +2 -2
- data/lib/faraday/request/retry.rb +65 -16
- data/lib/faraday/request.rb +22 -0
- data/lib/faraday/response/logger.rb +3 -3
- data/lib/faraday/response/raise_error.rb +7 -3
- data/lib/faraday/response.rb +3 -3
- data/lib/faraday/upload_io.rb +16 -6
- data/lib/faraday.rb +2 -3
- data/spec/faraday/deprecate_spec.rb +147 -0
- data/spec/faraday/error_spec.rb +102 -0
- data/spec/faraday/response/raise_error_spec.rb +106 -0
- data/spec/spec_helper.rb +105 -0
- data/test/adapters/default_test.rb +14 -0
- data/test/adapters/em_http_test.rb +30 -0
- data/test/adapters/em_synchrony_test.rb +32 -0
- data/test/adapters/excon_test.rb +30 -0
- data/test/adapters/httpclient_test.rb +34 -0
- data/test/adapters/integration.rb +263 -0
- data/test/adapters/logger_test.rb +136 -0
- data/test/adapters/net_http_persistent_test.rb +114 -0
- data/test/adapters/net_http_test.rb +79 -0
- data/test/adapters/patron_test.rb +40 -0
- data/test/adapters/rack_test.rb +38 -0
- data/test/adapters/test_middleware_test.rb +157 -0
- data/test/adapters/typhoeus_test.rb +38 -0
- data/test/authentication_middleware_test.rb +65 -0
- data/test/composite_read_io_test.rb +109 -0
- data/test/connection_test.rb +738 -0
- data/test/env_test.rb +268 -0
- data/test/helper.rb +75 -0
- data/test/live_server.rb +67 -0
- data/test/middleware/instrumentation_test.rb +88 -0
- data/test/middleware/retry_test.rb +282 -0
- data/test/middleware_stack_test.rb +260 -0
- data/test/multibyte.txt +1 -0
- data/test/options_test.rb +333 -0
- data/test/parameters_test.rb +157 -0
- data/test/request_middleware_test.rb +126 -0
- data/test/response_middleware_test.rb +72 -0
- data/test/strawberry.rb +2 -0
- data/test/utils_test.rb +98 -0
- metadata +48 -7
@@ -8,7 +8,8 @@ module Faraday
|
|
8
8
|
# TODO: support streaming requests
|
9
9
|
env[:body] = env[:body].read if env[:body].respond_to? :read
|
10
10
|
|
11
|
-
session =
|
11
|
+
session = ::Patron::Session.new
|
12
|
+
@config_block.call(session) if @config_block
|
12
13
|
configure_ssl(session, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
|
13
14
|
|
14
15
|
if req = env[:request]
|
@@ -27,7 +28,7 @@ module Faraday
|
|
27
28
|
data = env[:body] ? env[:body].to_s : nil
|
28
29
|
session.request(env[:method], env[:url].to_s, env[:request_headers], :data => data)
|
29
30
|
rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
|
30
|
-
raise
|
31
|
+
raise Faraday::ConnectionFailed, $!
|
31
32
|
end
|
32
33
|
|
33
34
|
# Remove the "HTTP/1.1 200", leaving just the reason phrase
|
@@ -38,15 +39,15 @@ module Faraday
|
|
38
39
|
@app.call env
|
39
40
|
rescue ::Patron::TimeoutError => err
|
40
41
|
if connection_timed_out_message?(err.message)
|
41
|
-
raise Faraday::
|
42
|
+
raise Faraday::ConnectionFailed, err
|
42
43
|
else
|
43
|
-
raise Faraday::
|
44
|
+
raise Faraday::TimeoutError, err
|
44
45
|
end
|
45
46
|
rescue ::Patron::Error => err
|
46
47
|
if err.message.include?("code 407")
|
47
|
-
raise
|
48
|
+
raise Faraday::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
48
49
|
else
|
49
|
-
raise
|
50
|
+
raise Faraday::ConnectionFailed, err
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -65,12 +66,6 @@ module Faraday
|
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
68
|
-
def create_session
|
69
|
-
session = ::Patron::Session.new
|
70
|
-
@config_block.call(session) if @config_block
|
71
|
-
session
|
72
|
-
end
|
73
|
-
|
74
69
|
def configure_ssl(session, ssl)
|
75
70
|
if ssl.fetch(:verify, true)
|
76
71
|
session.cacert = ssl[:ca_file]
|
data/lib/faraday/adapter/rack.rb
CHANGED
@@ -41,7 +41,7 @@ module Faraday
|
|
41
41
|
|
42
42
|
timeout = env[:request][:timeout] || env[:request][:open_timeout]
|
43
43
|
response = if timeout
|
44
|
-
Timer.timeout(timeout, Faraday::
|
44
|
+
Timer.timeout(timeout, Faraday::TimeoutError) { execute_request(env, rack_env) }
|
45
45
|
else
|
46
46
|
execute_request(env, rack_env)
|
47
47
|
end
|
data/lib/faraday/adapter.rb
CHANGED
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
# @param new_klass [Class] new Klass to use
|
5
|
+
#
|
6
|
+
# @return [Class] A modified version of new_klass that warns on
|
7
|
+
# usage about deprecation.
|
8
|
+
# @see Faraday::Deprecate
|
9
|
+
module DeprecatedClass
|
10
|
+
def self.proxy_class(origclass, ver = '1.0')
|
11
|
+
proxy = Class.new(origclass) do
|
12
|
+
const_set("ORIG_CLASS", origclass)
|
13
|
+
|
14
|
+
class << self
|
15
|
+
extend Faraday::Deprecate
|
16
|
+
|
17
|
+
def ===(other)
|
18
|
+
(superclass == const_get("ORIG_CLASS") && other.is_a?(superclass)) || super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
proxy.singleton_class.send(:deprecate, :new, "#{origclass}.new", ver)
|
23
|
+
proxy.singleton_class.send(:deprecate, :inherited, origclass.name, ver)
|
24
|
+
proxy
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Deprecation using semver instead of date, based on Gem::Deprecate
|
29
|
+
# Provides a single method +deprecate+ to be used to declare when
|
30
|
+
# something is going away.
|
31
|
+
#
|
32
|
+
# class Legacy
|
33
|
+
# def self.klass_method
|
34
|
+
# # ...
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def instance_method
|
38
|
+
# # ...
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# extend Faraday::Deprecate
|
42
|
+
# deprecate :instance_method, "X.z", '1.0'
|
43
|
+
#
|
44
|
+
# class << self
|
45
|
+
# extend Faraday::Deprecate
|
46
|
+
# deprecate :klass_method, :none, '1.0'
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
module Deprecate
|
50
|
+
def self.skip # :nodoc:
|
51
|
+
@skip ||= begin
|
52
|
+
case ENV['FARADAY_DEPRECATE'].to_s.downcase
|
53
|
+
when '1', 'warn' then :warn
|
54
|
+
else :skip
|
55
|
+
end
|
56
|
+
end
|
57
|
+
@skip == :skip
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.skip=(value) # :nodoc:
|
61
|
+
@skip = value ? :skip : :warn
|
62
|
+
end
|
63
|
+
|
64
|
+
# Temporarily turn off warnings. Intended for tests only.
|
65
|
+
def skip_during
|
66
|
+
original = Faraday::Deprecate.skip
|
67
|
+
Faraday::Deprecate.skip, = true
|
68
|
+
yield
|
69
|
+
ensure
|
70
|
+
Faraday::Deprecate.skip = original
|
71
|
+
end
|
72
|
+
|
73
|
+
# Simple deprecation method that deprecates +name+ by wrapping it up
|
74
|
+
# in a dummy method. It warns on each call to the dummy method
|
75
|
+
# telling the user of +repl+ (unless +repl+ is :none) and the
|
76
|
+
# semver that it is planned to go away.
|
77
|
+
# @param name [Symbol] the method symbol to deprecate
|
78
|
+
# @param repl [#to_s, :none] the replacement to use, when `:none` it will
|
79
|
+
# alert the user that no replacemtent is present.
|
80
|
+
# @param ver [String] the semver the method will be removed.
|
81
|
+
def deprecate(name, repl, ver)
|
82
|
+
class_eval do
|
83
|
+
gem_ver = Gem::Version.new(ver)
|
84
|
+
old = "_deprecated_#{name}"
|
85
|
+
alias_method old, name
|
86
|
+
define_method name do |*args, &block|
|
87
|
+
mod = is_a? Module
|
88
|
+
target = mod ? "#{self}." : "#{self.class}#"
|
89
|
+
target_message = if name == :inherited
|
90
|
+
"Inheriting #{self}"
|
91
|
+
else
|
92
|
+
"#{target}#{name}"
|
93
|
+
end
|
94
|
+
|
95
|
+
msg = [
|
96
|
+
"NOTE: #{target_message} is deprecated",
|
97
|
+
repl == :none ? ' with no replacement' : "; use #{repl} instead. ",
|
98
|
+
"It will be removed in or after version #{gem_ver}",
|
99
|
+
"\n#{target}#{name} called from #{Gem.location_of_caller.join(':')}"
|
100
|
+
]
|
101
|
+
warn "#{msg.join}." unless Faraday::Deprecate.skip
|
102
|
+
send old, *args, &block
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module_function :deprecate, :skip_during
|
108
|
+
end
|
109
|
+
end
|
data/lib/faraday/error.rb
CHANGED
@@ -1,23 +1,17 @@
|
|
1
|
-
|
2
|
-
class Error < StandardError; end
|
3
|
-
class MissingDependency < Error; end
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
attr_reader :response, :wrapped_exception
|
3
|
+
require 'faraday/deprecate'
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
# Faraday namespace.
|
6
|
+
module Faraday
|
7
|
+
# Faraday error base class.
|
8
|
+
class Error < StandardError
|
9
|
+
attr_reader :response, :wrapped_exception
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
super("the server responded with status #{ex[:status]}")
|
17
|
-
@response = ex
|
18
|
-
else
|
19
|
-
super(ex.to_s)
|
20
|
-
end
|
11
|
+
def initialize(exc, response = nil)
|
12
|
+
@wrapped_exception = nil unless defined?(@wrapped_exception)
|
13
|
+
@response = nil unless defined?(@response)
|
14
|
+
super(exc_msg_and_response!(exc, response))
|
21
15
|
end
|
22
16
|
|
23
17
|
def backtrace
|
@@ -30,34 +24,135 @@ module Faraday
|
|
30
24
|
|
31
25
|
def inspect
|
32
26
|
inner = ''
|
33
|
-
if @wrapped_exception
|
34
|
-
|
35
|
-
|
36
|
-
if @response
|
37
|
-
inner << " response=#{@response.inspect}"
|
38
|
-
end
|
39
|
-
if inner.empty?
|
40
|
-
inner << " #{super}"
|
41
|
-
end
|
27
|
+
inner += " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
|
28
|
+
inner += " response=#{@response.inspect}" if @response
|
29
|
+
inner += " #{super}" if inner.empty?
|
42
30
|
%(#<#{self.class}#{inner}>)
|
43
31
|
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
# Pulls out potential parent exception and response hash, storing them in
|
36
|
+
# instance variables.
|
37
|
+
# exc - Either an Exception, a string message, or a response hash.
|
38
|
+
# response - Hash
|
39
|
+
# :status - Optional integer HTTP response status
|
40
|
+
# :headers - String key/value hash of HTTP response header
|
41
|
+
# values.
|
42
|
+
# :body - Optional string HTTP response body.
|
43
|
+
#
|
44
|
+
# If a subclass has to call this, then it should pass a string message
|
45
|
+
# to `super`. See NilStatusError.
|
46
|
+
def exc_msg_and_response!(exc, response = nil)
|
47
|
+
if @response.nil? && @wrapped_exception.nil?
|
48
|
+
@wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
|
49
|
+
return msg
|
50
|
+
end
|
51
|
+
|
52
|
+
exc.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
# Pulls out potential parent exception and response hash.
|
56
|
+
def exc_msg_and_response(exc, response = nil)
|
57
|
+
return [exc, exc.message, response] if exc.respond_to?(:backtrace)
|
58
|
+
|
59
|
+
return [nil, "the server responded with status #{exc[:status]}", exc] \
|
60
|
+
if exc.respond_to?(:each_key)
|
61
|
+
|
62
|
+
[nil, exc.to_s, response]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Faraday client error class. Represents 4xx status responses.
|
67
|
+
class ClientError < Error
|
68
|
+
end
|
69
|
+
|
70
|
+
# Raised by Faraday::Response::RaiseError in case of a 400 response.
|
71
|
+
class BadRequestError < ClientError
|
72
|
+
end
|
73
|
+
|
74
|
+
# Raised by Faraday::Response::RaiseError in case of a 401 response.
|
75
|
+
class UnauthorizedError < ClientError
|
44
76
|
end
|
45
77
|
|
46
|
-
|
47
|
-
class
|
48
|
-
|
78
|
+
# Raised by Faraday::Response::RaiseError in case of a 403 response.
|
79
|
+
class ForbiddenError < ClientError
|
80
|
+
end
|
81
|
+
|
82
|
+
# Raised by Faraday::Response::RaiseError in case of a 404 response.
|
83
|
+
class ResourceNotFound < ClientError
|
84
|
+
end
|
85
|
+
|
86
|
+
# Raised by Faraday::Response::RaiseError in case of a 407 response.
|
87
|
+
class ProxyAuthError < ClientError
|
88
|
+
end
|
89
|
+
|
90
|
+
# Raised by Faraday::Response::RaiseError in case of a 409 response.
|
91
|
+
class ConflictError < ClientError
|
92
|
+
end
|
49
93
|
|
94
|
+
# Raised by Faraday::Response::RaiseError in case of a 422 response.
|
95
|
+
class UnprocessableEntityError < ClientError
|
96
|
+
end
|
97
|
+
|
98
|
+
# Faraday server error class. Represents 5xx status responses.
|
99
|
+
class ServerError < Error
|
100
|
+
end
|
101
|
+
|
102
|
+
# A unified client error for timeouts.
|
50
103
|
class TimeoutError < ClientError
|
51
|
-
def initialize(
|
52
|
-
super(
|
104
|
+
def initialize(exc = 'timeout', response = nil)
|
105
|
+
super(exc, response)
|
53
106
|
end
|
54
107
|
end
|
55
108
|
|
109
|
+
# Raised by Faraday::Response::RaiseError in case of a nil status in response.
|
110
|
+
class NilStatusError < ServerError
|
111
|
+
def initialize(exc, response = nil)
|
112
|
+
exc_msg_and_response!(exc, response)
|
113
|
+
@response = unwrap_resp!(@response)
|
114
|
+
super('http status could not be derived from the server response')
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
extend Faraday::Deprecate
|
120
|
+
|
121
|
+
def unwrap_resp(resp)
|
122
|
+
if inner = (resp.keys.size == 1 && resp[:response])
|
123
|
+
return unwrap_resp(inner)
|
124
|
+
end
|
125
|
+
|
126
|
+
resp
|
127
|
+
end
|
128
|
+
|
129
|
+
alias_method :unwrap_resp!, :unwrap_resp
|
130
|
+
deprecate('unwrap_resp', nil, '1.0')
|
131
|
+
end
|
132
|
+
|
133
|
+
# A unified error for failed connections.
|
134
|
+
class ConnectionFailed < ClientError
|
135
|
+
end
|
136
|
+
|
137
|
+
# A unified client error for SSL errors.
|
56
138
|
class SSLError < ClientError
|
57
139
|
end
|
58
140
|
|
59
|
-
|
60
|
-
|
61
|
-
|
141
|
+
# Raised by FaradayMiddleware::ResponseMiddleware
|
142
|
+
class ParsingError < ClientError
|
143
|
+
end
|
144
|
+
|
145
|
+
# Exception used to control the Retry middleware.
|
146
|
+
#
|
147
|
+
# @see Faraday::Request::Retry
|
148
|
+
class RetriableResponse < ClientError
|
149
|
+
end
|
150
|
+
|
151
|
+
[:ClientError, :ConnectionFailed, :ResourceNotFound,
|
152
|
+
:ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const|
|
153
|
+
Error.const_set(
|
154
|
+
const,
|
155
|
+
DeprecatedClass.proxy_class(Faraday.const_get(const))
|
156
|
+
)
|
62
157
|
end
|
63
158
|
end
|
data/lib/faraday/options.rb
CHANGED
@@ -72,7 +72,7 @@ module Faraday
|
|
72
72
|
if args.size > 0
|
73
73
|
send(key_setter, args.first)
|
74
74
|
elsif block_given?
|
75
|
-
send(key_setter,
|
75
|
+
send(key_setter, yield(key))
|
76
76
|
else
|
77
77
|
raise self.class.fetch_error_class, "key not found: #{key.inspect}"
|
78
78
|
end
|
@@ -162,8 +162,8 @@ module Faraday
|
|
162
162
|
@attribute_options ||= {}
|
163
163
|
end
|
164
164
|
|
165
|
-
def self.memoized(key)
|
166
|
-
memoized_attributes[key.to_sym] =
|
165
|
+
def self.memoized(key, &block)
|
166
|
+
memoized_attributes[key.to_sym] = block
|
167
167
|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
168
168
|
def #{key}() self[:#{key}]; end
|
169
169
|
RUBY
|
@@ -202,7 +202,7 @@ module Faraday
|
|
202
202
|
end
|
203
203
|
|
204
204
|
class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
|
205
|
-
:timeout, :open_timeout, :boundary, :oauth, :context)
|
205
|
+
:timeout, :open_timeout, :write_timeout, :boundary, :oauth, :context)
|
206
206
|
|
207
207
|
def []=(key, value)
|
208
208
|
if key && key.to_sym == :proxy
|
@@ -214,7 +214,8 @@ module Faraday
|
|
214
214
|
end
|
215
215
|
|
216
216
|
class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
|
217
|
-
:cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth,
|
217
|
+
:cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth,
|
218
|
+
:version, :min_version, :max_version)
|
218
219
|
|
219
220
|
def verify?
|
220
221
|
verify != false
|
data/lib/faraday/parameters.rb
CHANGED
@@ -40,9 +40,10 @@ module Faraday
|
|
40
40
|
end
|
41
41
|
return buffer.chop
|
42
42
|
elsif value.is_a?(Array)
|
43
|
+
new_parent = "#{parent}%5B%5D"
|
44
|
+
return new_parent if value.empty?
|
43
45
|
buffer = ""
|
44
46
|
value.each_with_index do |val, i|
|
45
|
-
new_parent = "#{parent}%5B%5D"
|
46
47
|
buffer << "#{to_query.call(new_parent, val)}&"
|
47
48
|
end
|
48
49
|
return buffer.chop
|
data/lib/faraday/rack_builder.rb
CHANGED
@@ -49,10 +49,10 @@ module Faraday
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
def initialize(handlers = [])
|
52
|
+
def initialize(handlers = [], &block)
|
53
53
|
@handlers = handlers
|
54
54
|
if block_given?
|
55
|
-
build(&
|
55
|
+
build(&block)
|
56
56
|
elsif @handlers.empty?
|
57
57
|
# default stack, if nothing else is configured
|
58
58
|
self.request :url_encoded
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
1
3
|
module Faraday
|
2
4
|
# Catches exceptions and retries each request a limited number of times.
|
3
5
|
#
|
@@ -19,11 +21,15 @@ module Faraday
|
|
19
21
|
# interval that is random between 0.1 and 0.15
|
20
22
|
#
|
21
23
|
class Request::Retry < Faraday::Middleware
|
22
|
-
|
24
|
+
DEFAULT_EXCEPTIONS = [Errno::ETIMEDOUT, 'Timeout::Error',
|
25
|
+
Faraday::TimeoutError, Faraday::RetriableResponse
|
26
|
+
].freeze
|
23
27
|
IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put]
|
24
28
|
|
25
29
|
class Options < Faraday::Options.new(:max, :interval, :max_interval, :interval_randomness,
|
26
|
-
:backoff_factor, :exceptions, :methods, :retry_if
|
30
|
+
:backoff_factor, :exceptions, :methods, :retry_if, :retry_block,
|
31
|
+
:retry_statuses)
|
32
|
+
|
27
33
|
DEFAULT_CHECK = lambda { |env,exception| false }
|
28
34
|
|
29
35
|
def self.from(value)
|
@@ -55,8 +61,7 @@ module Faraday
|
|
55
61
|
end
|
56
62
|
|
57
63
|
def exceptions
|
58
|
-
Array(self[:exceptions] ||=
|
59
|
-
Error::TimeoutError])
|
64
|
+
Array(self[:exceptions] ||= DEFAULT_EXCEPTIONS)
|
60
65
|
end
|
61
66
|
|
62
67
|
def methods
|
@@ -67,6 +72,13 @@ module Faraday
|
|
67
72
|
self[:retry_if] ||= DEFAULT_CHECK
|
68
73
|
end
|
69
74
|
|
75
|
+
def retry_block
|
76
|
+
self[:retry_block] ||= Proc.new {}
|
77
|
+
end
|
78
|
+
|
79
|
+
def retry_statuses
|
80
|
+
Array(self[:retry_statuses] ||= [])
|
81
|
+
end
|
70
82
|
end
|
71
83
|
|
72
84
|
# Public: Initialize middleware
|
@@ -83,8 +95,8 @@ module Faraday
|
|
83
95
|
# (default: 1)
|
84
96
|
# exceptions - The list of exceptions to handle. Exceptions can be
|
85
97
|
# given as Class, Module, or String. (default:
|
86
|
-
# [Errno::ETIMEDOUT, Timeout::Error,
|
87
|
-
#
|
98
|
+
# [Errno::ETIMEDOUT, 'Timeout::Error',
|
99
|
+
# Faraday::TimeoutError, Faraday::RetriableResponse])
|
88
100
|
# methods - A list of HTTP methods to retry without calling retry_if. Pass
|
89
101
|
# an empty Array to call retry_if for all exceptions.
|
90
102
|
# (defaults to the idempotent HTTP methods in IDEMPOTENT_METHODS)
|
@@ -94,18 +106,21 @@ module Faraday
|
|
94
106
|
# if the exception produced is non-recoverable or if the
|
95
107
|
# the HTTP method called is not idempotent.
|
96
108
|
# (defaults to return false)
|
109
|
+
# retry_block - block that is executed after every retry. Request environment, middleware options,
|
110
|
+
# current number of retries and the exception is passed to the block as parameters.
|
97
111
|
def initialize(app, options = nil)
|
98
112
|
super(app)
|
99
113
|
@options = Options.from(options)
|
100
114
|
@errmatch = build_exception_matcher(@options.exceptions)
|
101
115
|
end
|
102
116
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
117
|
+
def calculate_sleep_amount(retries, env)
|
118
|
+
retry_after = calculate_retry_after(env)
|
119
|
+
retry_interval = calculate_retry_interval(retries)
|
120
|
+
|
121
|
+
return if retry_after && retry_after > @options.max_interval
|
122
|
+
|
123
|
+
retry_after && retry_after >= retry_interval ? retry_after : retry_interval
|
109
124
|
end
|
110
125
|
|
111
126
|
def call(env)
|
@@ -113,15 +128,25 @@ module Faraday
|
|
113
128
|
request_body = env[:body]
|
114
129
|
begin
|
115
130
|
env[:body] = request_body # after failure env[:body] is set to the response body
|
116
|
-
@app.call(env)
|
131
|
+
@app.call(env).tap do |resp|
|
132
|
+
raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
|
133
|
+
end
|
117
134
|
rescue @errmatch => exception
|
118
135
|
if retries > 0 && retry_request?(env, exception)
|
119
136
|
retries -= 1
|
120
137
|
rewind_files(request_body)
|
121
|
-
|
122
|
-
|
138
|
+
@options.retry_block.call(env, @options, retries, exception)
|
139
|
+
if (sleep_amount = calculate_sleep_amount(retries + 1, env))
|
140
|
+
sleep sleep_amount
|
141
|
+
retry
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
if exception.is_a?(Faraday::RetriableResponse)
|
146
|
+
exception.response
|
147
|
+
else
|
148
|
+
raise
|
123
149
|
end
|
124
|
-
raise
|
125
150
|
end
|
126
151
|
end
|
127
152
|
|
@@ -160,5 +185,29 @@ module Faraday
|
|
160
185
|
end
|
161
186
|
end
|
162
187
|
|
188
|
+
# MDN spec for Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
|
189
|
+
def calculate_retry_after(env)
|
190
|
+
response_headers = env[:response_headers]
|
191
|
+
return unless response_headers
|
192
|
+
|
193
|
+
retry_after_value = env[:response_headers]["Retry-After"]
|
194
|
+
|
195
|
+
# Try to parse date from the header value
|
196
|
+
begin
|
197
|
+
datetime = DateTime.rfc2822(retry_after_value)
|
198
|
+
datetime.to_time - Time.now.utc
|
199
|
+
rescue ArgumentError
|
200
|
+
retry_after_value.to_f
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def calculate_retry_interval(retries)
|
205
|
+
retry_index = @options.max - retries
|
206
|
+
current_interval = @options.interval * (@options.backoff_factor ** retry_index)
|
207
|
+
current_interval = [current_interval, @options.max_interval].min
|
208
|
+
random_interval = rand * @options.interval_randomness.to_f * @options.interval
|
209
|
+
|
210
|
+
current_interval + random_interval
|
211
|
+
end
|
163
212
|
end
|
164
213
|
end
|
data/lib/faraday/request.rb
CHANGED
@@ -12,6 +12,8 @@ module Faraday
|
|
12
12
|
class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
|
13
13
|
extend MiddlewareRegistry
|
14
14
|
|
15
|
+
alias_method :http_method, :method
|
16
|
+
|
15
17
|
register_middleware File.expand_path('../request', __FILE__),
|
16
18
|
:url_encoded => [:UrlEncoded, 'url_encoded'],
|
17
19
|
:multipart => [:Multipart, 'multipart'],
|
@@ -69,6 +71,26 @@ module Faraday
|
|
69
71
|
headers[key] = value
|
70
72
|
end
|
71
73
|
|
74
|
+
def marshal_dump
|
75
|
+
{
|
76
|
+
:method => method,
|
77
|
+
:body => body,
|
78
|
+
:headers => headers,
|
79
|
+
:path => path,
|
80
|
+
:params => params,
|
81
|
+
:options => options
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def marshal_load(serialised)
|
86
|
+
self.method = serialised[:method]
|
87
|
+
self.body = serialised[:body]
|
88
|
+
self.headers = serialised[:headers]
|
89
|
+
self.path = serialised[:path]
|
90
|
+
self.params = serialised[:params]
|
91
|
+
self.options = serialised[:options]
|
92
|
+
end
|
93
|
+
|
72
94
|
# ENV Keys
|
73
95
|
# :method - a symbolized request method (:get, :post)
|
74
96
|
# :body - the request body that will eventually be converted to a string.
|
@@ -10,7 +10,7 @@ module Faraday
|
|
10
10
|
super(app)
|
11
11
|
@logger = logger || begin
|
12
12
|
require 'logger'
|
13
|
-
::Logger.new(
|
13
|
+
::Logger.new($stdout)
|
14
14
|
end
|
15
15
|
@filter = []
|
16
16
|
@options = DEFAULT_OPTIONS.merge(options)
|
@@ -20,14 +20,14 @@ module Faraday
|
|
20
20
|
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
21
21
|
|
22
22
|
def call(env)
|
23
|
-
info "#{env.method} #{apply_filters(env.url.to_s)}"
|
23
|
+
info('request') { "#{env.method.upcase} #{apply_filters(env.url.to_s)}" }
|
24
24
|
debug('request') { apply_filters( dump_headers env.request_headers ) } if log_headers?(:request)
|
25
25
|
debug('request') { apply_filters( dump_body(env[:body]) ) } if env[:body] && log_body?(:request)
|
26
26
|
super
|
27
27
|
end
|
28
28
|
|
29
29
|
def on_complete(env)
|
30
|
-
info('
|
30
|
+
info('response') { "Status #{env.status.to_s}" }
|
31
31
|
debug('response') { apply_filters( dump_headers env.response_headers ) } if log_headers?(:response)
|
32
32
|
debug('response') { apply_filters( dump_body env[:body] ) } if env[:body] && log_body?(:response)
|
33
33
|
end
|
@@ -5,12 +5,16 @@ module Faraday
|
|
5
5
|
def on_complete(env)
|
6
6
|
case env[:status]
|
7
7
|
when 404
|
8
|
-
raise Faraday::
|
8
|
+
raise Faraday::ResourceNotFound, response_values(env)
|
9
9
|
when 407
|
10
10
|
# mimic the behavior that we get with proxy requests with HTTPS
|
11
|
-
raise Faraday::
|
11
|
+
raise Faraday::ConnectionFailed.new(
|
12
|
+
%{407 "Proxy Authentication Required "},
|
13
|
+
response_values(env))
|
12
14
|
when ClientErrorStatuses
|
13
|
-
raise Faraday::
|
15
|
+
raise Faraday::ClientError, response_values(env)
|
16
|
+
when nil
|
17
|
+
raise Faraday::NilStatusError, response_values(env)
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
data/lib/faraday/response.rb
CHANGED