faraday 0.8.11 → 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.
- checksums.yaml +4 -4
- data/.document +6 -0
- data/CHANGELOG.md +15 -0
- data/CONTRIBUTING.md +36 -0
- data/Gemfile +10 -3
- data/LICENSE.md +1 -1
- data/README.md +17 -51
- data/Rakefile +2 -18
- data/faraday.gemspec +34 -0
- data/lib/faraday/adapter/em_http.rb +34 -0
- data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
- data/lib/faraday/adapter/em_synchrony.rb +11 -24
- data/lib/faraday/adapter/excon.rb +12 -2
- data/lib/faraday/adapter/httpclient.rb +106 -0
- data/lib/faraday/adapter/net_http.rb +10 -6
- data/lib/faraday/adapter/patron.rb +2 -8
- data/lib/faraday/adapter/rack.rb +0 -2
- data/lib/faraday/adapter/test.rb +39 -39
- data/lib/faraday/adapter/typhoeus.rb +12 -3
- data/lib/faraday/adapter.rb +20 -35
- data/lib/faraday/autoload.rb +85 -0
- data/lib/faraday/connection.rb +232 -125
- data/lib/faraday/error.rb +42 -34
- data/lib/faraday/options.rb +350 -0
- data/lib/faraday/parameters.rb +193 -0
- data/lib/faraday/{builder.rb → rack_builder.rb} +79 -22
- data/lib/faraday/request/authorization.rb +7 -5
- data/lib/faraday/request/basic_authentication.rb +1 -1
- data/lib/faraday/request/instrumentation.rb +36 -0
- data/lib/faraday/request/multipart.rb +10 -9
- data/lib/faraday/request/retry.rb +99 -4
- data/lib/faraday/request/token_authentication.rb +2 -2
- data/lib/faraday/request/url_encoded.rb +7 -6
- data/lib/faraday/request.rb +21 -30
- data/lib/faraday/response/logger.rb +4 -4
- data/lib/faraday/response/raise_error.rb +4 -2
- data/lib/faraday/response.rb +17 -23
- data/lib/faraday/utils.rb +81 -71
- data/lib/faraday.rb +187 -68
- data/script/console +7 -0
- data/script/proxy-server +1 -0
- data/script/release +6 -3
- data/script/test +4 -2
- data/test/adapters/em_http_test.rb +6 -1
- data/test/adapters/em_synchrony_test.rb +7 -1
- data/test/adapters/httpclient_test.rb +21 -0
- data/test/adapters/integration.rb +23 -8
- data/test/adapters/logger_test.rb +1 -1
- data/test/adapters/net_http_persistent_test.rb +10 -1
- data/test/adapters/net_http_test.rb +0 -31
- data/test/adapters/patron_test.rb +4 -1
- data/test/adapters/test_middleware_test.rb +48 -4
- data/test/adapters/typhoeus_test.rb +8 -1
- data/test/authentication_middleware_test.rb +2 -2
- data/test/connection_test.rb +160 -84
- data/test/env_test.rb +51 -24
- data/test/helper.rb +13 -13
- data/test/live_server.rb +8 -0
- data/test/middleware/instrumentation_test.rb +88 -0
- data/test/middleware/retry_test.rb +88 -35
- data/test/middleware_stack_test.rb +13 -12
- data/test/options_test.rb +252 -0
- data/test/request_middleware_test.rb +11 -1
- data/test/response_middleware_test.rb +2 -4
- data/test/strawberry.rb +2 -0
- data/test/utils_test.rb +34 -6
- metadata +71 -11
- data/test/parameters_test.rb +0 -24
@@ -0,0 +1,36 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::Instrumentation < Faraday::Middleware
|
3
|
+
class Options < Faraday::Options.new(:name, :instrumenter)
|
4
|
+
def name
|
5
|
+
self[:name] ||= 'request.faraday'
|
6
|
+
end
|
7
|
+
|
8
|
+
def instrumenter
|
9
|
+
self[:instrumenter] ||= ActiveSupport::Notifications
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: Instruments requests using Active Support.
|
14
|
+
#
|
15
|
+
# Measures time spent only for synchronous requests.
|
16
|
+
#
|
17
|
+
# Examples
|
18
|
+
#
|
19
|
+
# ActiveSupport::Notifications.subscribe('request.faraday') do |name, starts, ends, _, env|
|
20
|
+
# url = env[:url]
|
21
|
+
# http_method = env[:method].to_s.upcase
|
22
|
+
# duration = ends - starts
|
23
|
+
# $stderr.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration]
|
24
|
+
# end
|
25
|
+
def initialize(app, options = nil)
|
26
|
+
super(app)
|
27
|
+
@name, @instrumenter = Options.from(options).values_at(:name, :instrumenter)
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(env)
|
31
|
+
@instrumenter.instrument(@name, env) do
|
32
|
+
@app.call(env)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,22 +1,23 @@
|
|
1
|
+
require File.expand_path("../url_encoded", __FILE__)
|
2
|
+
|
1
3
|
module Faraday
|
2
4
|
class Request::Multipart < Request::UrlEncoded
|
3
5
|
self.mime_type = 'multipart/form-data'.freeze
|
4
|
-
DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze
|
6
|
+
DEFAULT_BOUNDARY = "-----------RubyMultipartPost".freeze unless defined? DEFAULT_BOUNDARY
|
5
7
|
|
6
8
|
def call(env)
|
7
9
|
match_content_type(env) do |params|
|
8
|
-
env
|
9
|
-
env[
|
10
|
-
env
|
11
|
-
env[:body] = create_multipart(env, params)
|
10
|
+
env.request.boundary ||= DEFAULT_BOUNDARY
|
11
|
+
env.request_headers[CONTENT_TYPE] += "; boundary=#{env.request.boundary}"
|
12
|
+
env.body = create_multipart(env, params)
|
12
13
|
end
|
13
14
|
@app.call env
|
14
15
|
end
|
15
16
|
|
16
17
|
def process_request?(env)
|
17
18
|
type = request_type(env)
|
18
|
-
env
|
19
|
-
(type.empty? and has_multipart?(env
|
19
|
+
env.body.respond_to?(:each_key) and !env.body.empty? and (
|
20
|
+
(type.empty? and has_multipart?(env.body)) or
|
20
21
|
type == self.class.mime_type
|
21
22
|
)
|
22
23
|
end
|
@@ -32,14 +33,14 @@ module Faraday
|
|
32
33
|
end
|
33
34
|
|
34
35
|
def create_multipart(env, params)
|
35
|
-
boundary = env
|
36
|
+
boundary = env.request.boundary
|
36
37
|
parts = process_params(params) do |key, value|
|
37
38
|
Faraday::Parts::Part.new(boundary, key, value)
|
38
39
|
end
|
39
40
|
parts << Faraday::Parts::EpiloguePart.new(boundary)
|
40
41
|
|
41
42
|
body = Faraday::CompositeReadIO.new(parts)
|
42
|
-
env
|
43
|
+
env.request_headers[Faraday::Env::ContentLength] = body.length.to_s
|
43
44
|
return body
|
44
45
|
end
|
45
46
|
|
@@ -1,23 +1,118 @@
|
|
1
1
|
module Faraday
|
2
|
+
# Catches exceptions and retries each request a limited number of times.
|
3
|
+
#
|
4
|
+
# By default, it retries 2 times and handles only timeout exceptions. It can
|
5
|
+
# be configured with an arbitrary number of retries, a list of exceptions to
|
6
|
+
# handle, a retry interval, a percentage of randomness to add to the retry
|
7
|
+
# interval, and a backoff factor.
|
8
|
+
#
|
9
|
+
# Examples
|
10
|
+
#
|
11
|
+
# Faraday.new do |conn|
|
12
|
+
# conn.request :retry, max: 2, interval: 0.05,
|
13
|
+
# interval_randomness: 0.5, backoff_factor: 2
|
14
|
+
# exceptions: [CustomException, 'Timeout::Error']
|
15
|
+
# conn.adapter ...
|
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
|
+
#
|
2
21
|
class Request::Retry < Faraday::Middleware
|
3
|
-
|
4
|
-
|
22
|
+
class Options < Faraday::Options.new(:max, :interval, :interval_randomness, :backoff_factor, :exceptions)
|
23
|
+
def self.from(value)
|
24
|
+
if Fixnum === value
|
25
|
+
new(value)
|
26
|
+
else
|
27
|
+
super(value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def max
|
32
|
+
(self[:max] ||= 2).to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
def interval
|
36
|
+
(self[:interval] ||= 0).to_f
|
37
|
+
end
|
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
|
+
|
47
|
+
def exceptions
|
48
|
+
Array(self[:exceptions] ||= [Errno::ETIMEDOUT, 'Timeout::Error',
|
49
|
+
Error::TimeoutError])
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
# Public: Initialize middleware
|
55
|
+
#
|
56
|
+
# Options:
|
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])
|
69
|
+
def initialize(app, options = nil)
|
5
70
|
super(app)
|
71
|
+
@options = Options.from(options)
|
72
|
+
@errmatch = build_exception_matcher(@options.exceptions)
|
73
|
+
end
|
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
|
6
80
|
end
|
7
81
|
|
8
82
|
def call(env)
|
9
|
-
retries = @
|
83
|
+
retries = @options.max
|
10
84
|
request_body = env[:body]
|
11
85
|
begin
|
12
86
|
env[:body] = request_body # after failure env[:body] is set to the response body
|
13
87
|
@app.call(env)
|
14
|
-
rescue
|
88
|
+
rescue @errmatch
|
15
89
|
if retries > 0
|
16
90
|
retries -= 1
|
91
|
+
sleep sleep_amount(retries + 1)
|
17
92
|
retry
|
18
93
|
end
|
19
94
|
raise
|
20
95
|
end
|
21
96
|
end
|
97
|
+
|
98
|
+
# Private: construct an exception matcher object.
|
99
|
+
#
|
100
|
+
# An exception matcher for the rescue clause can usually be any object that
|
101
|
+
# responds to `===`, but for Ruby 1.8 it has to be a Class or Module.
|
102
|
+
def build_exception_matcher(exceptions)
|
103
|
+
matcher = Module.new
|
104
|
+
(class << matcher; self; end).class_eval do
|
105
|
+
define_method(:===) do |error|
|
106
|
+
exceptions.any? do |ex|
|
107
|
+
if ex.is_a? Module
|
108
|
+
error.is_a? ex
|
109
|
+
else
|
110
|
+
error.class.to_s == ex.to_s
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
matcher
|
116
|
+
end
|
22
117
|
end
|
23
118
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module Faraday
|
2
|
-
class Request::TokenAuthentication < Request
|
2
|
+
class Request::TokenAuthentication < Request.load_middleware(:authorization)
|
3
3
|
# Public
|
4
4
|
def self.header(token, options = nil)
|
5
5
|
options ||= {}
|
6
6
|
options[:token] = token
|
7
|
-
super
|
7
|
+
super(:Token, options)
|
8
8
|
end
|
9
9
|
|
10
10
|
def initialize(app, token, options = nil)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Faraday
|
2
2
|
class Request::UrlEncoded < Faraday::Middleware
|
3
|
-
CONTENT_TYPE = 'Content-Type'.freeze
|
3
|
+
CONTENT_TYPE = 'Content-Type'.freeze unless defined? CONTENT_TYPE
|
4
4
|
|
5
5
|
class << self
|
6
6
|
attr_accessor :mime_type
|
@@ -9,25 +9,26 @@ module Faraday
|
|
9
9
|
|
10
10
|
def call(env)
|
11
11
|
match_content_type(env) do |data|
|
12
|
-
|
12
|
+
params = Faraday::Utils::ParamsHash[data]
|
13
|
+
env.body = params.to_query(env.params_encoder)
|
13
14
|
end
|
14
15
|
@app.call env
|
15
16
|
end
|
16
17
|
|
17
18
|
def match_content_type(env)
|
18
19
|
if process_request?(env)
|
19
|
-
env
|
20
|
-
yield
|
20
|
+
env.request_headers[CONTENT_TYPE] ||= self.class.mime_type
|
21
|
+
yield(env.body) unless env.body.respond_to?(:to_str)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
24
25
|
def process_request?(env)
|
25
26
|
type = request_type(env)
|
26
|
-
env
|
27
|
+
env.body and (type.empty? or type == self.class.mime_type)
|
27
28
|
end
|
28
29
|
|
29
30
|
def request_type(env)
|
30
|
-
type = env
|
31
|
+
type = env.request_headers[CONTENT_TYPE].to_s
|
31
32
|
type = type.split(';', 2).first if type.index(';')
|
32
33
|
type
|
33
34
|
end
|
data/lib/faraday/request.rb
CHANGED
@@ -10,43 +10,38 @@ module Faraday
|
|
10
10
|
# end
|
11
11
|
#
|
12
12
|
class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
|
13
|
-
extend AutoloadHelper
|
14
13
|
extend MiddlewareRegistry
|
15
14
|
|
16
|
-
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
|
25
|
-
register_middleware \
|
26
|
-
:url_encoded => :UrlEncoded,
|
27
|
-
:multipart => :Multipart,
|
28
|
-
:retry => :Retry,
|
29
|
-
:authorization => :Authorization,
|
30
|
-
:basic_auth => :BasicAuthentication,
|
31
|
-
:token_auth => :TokenAuthentication
|
15
|
+
register_middleware File.expand_path('../request', __FILE__),
|
16
|
+
:url_encoded => [:UrlEncoded, 'url_encoded'],
|
17
|
+
:multipart => [:Multipart, 'multipart'],
|
18
|
+
:retry => [:Retry, 'retry'],
|
19
|
+
:authorization => [:Authorization, 'authorization'],
|
20
|
+
:basic_auth => [:BasicAuthentication, 'basic_authentication'],
|
21
|
+
:token_auth => [:TokenAuthentication, 'token_authentication'],
|
22
|
+
:instrumentation => [:Instrumentation, 'instrumentation']
|
32
23
|
|
33
24
|
def self.create(request_method)
|
34
25
|
new(request_method).tap do |request|
|
35
|
-
yield
|
26
|
+
yield(request) if block_given?
|
36
27
|
end
|
37
28
|
end
|
38
29
|
|
39
30
|
# Public: Replace params, preserving the existing hash type
|
40
31
|
def params=(hash)
|
41
|
-
if params
|
42
|
-
|
32
|
+
if params
|
33
|
+
params.replace hash
|
34
|
+
else
|
35
|
+
super
|
43
36
|
end
|
44
37
|
end
|
45
38
|
|
46
39
|
# Public: Replace request headers, preserving the existing hash type
|
47
40
|
def headers=(hash)
|
48
|
-
if headers
|
49
|
-
|
41
|
+
if headers
|
42
|
+
headers.replace hash
|
43
|
+
else
|
44
|
+
super
|
50
45
|
end
|
51
46
|
end
|
52
47
|
|
@@ -60,7 +55,7 @@ module Faraday
|
|
60
55
|
path, query = path.split('?', 2)
|
61
56
|
end
|
62
57
|
self.path = path
|
63
|
-
self.params.merge_query query
|
58
|
+
self.params.merge_query query, options.params_encoder
|
64
59
|
self.params.update(params) if params
|
65
60
|
end
|
66
61
|
|
@@ -89,13 +84,9 @@ module Faraday
|
|
89
84
|
# :password - Proxy server password
|
90
85
|
# :ssl - Hash of options for configuring SSL requests.
|
91
86
|
def to_env(connection)
|
92
|
-
|
93
|
-
|
94
|
-
:url => connection.build_exclusive_url(path, params),
|
95
|
-
:request_headers => headers,
|
96
|
-
:parallel_manager => connection.parallel_manager,
|
97
|
-
:request => options,
|
98
|
-
:ssl => connection.ssl}
|
87
|
+
Env.new(method, body, connection.build_exclusive_url(path, params),
|
88
|
+
options, headers, connection.ssl, connection.parallel_manager)
|
99
89
|
end
|
100
90
|
end
|
101
91
|
end
|
92
|
+
|
@@ -15,14 +15,14 @@ module Faraday
|
|
15
15
|
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
16
16
|
|
17
17
|
def call(env)
|
18
|
-
info "#{env
|
19
|
-
debug('request') { dump_headers env
|
18
|
+
info "#{env.method} #{env.url.to_s}"
|
19
|
+
debug('request') { dump_headers env.request_headers }
|
20
20
|
super
|
21
21
|
end
|
22
22
|
|
23
23
|
def on_complete(env)
|
24
|
-
info('Status') { env
|
25
|
-
debug('response') { dump_headers env
|
24
|
+
info('Status') { env.status.to_s }
|
25
|
+
debug('response') { dump_headers env.response_headers }
|
26
26
|
end
|
27
27
|
|
28
28
|
private
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Faraday
|
2
2
|
class Response::RaiseError < Response::Middleware
|
3
|
+
ClientErrorStatuses = 400...600
|
4
|
+
|
3
5
|
def on_complete(env)
|
4
6
|
case env[:status]
|
5
7
|
when 404
|
@@ -7,13 +9,13 @@ module Faraday
|
|
7
9
|
when 407
|
8
10
|
# mimic the behavior that we get with proxy requests with HTTPS
|
9
11
|
raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
10
|
-
when
|
12
|
+
when ClientErrorStatuses
|
11
13
|
raise Faraday::Error::ClientError, response_values(env)
|
12
14
|
end
|
13
15
|
end
|
14
16
|
|
15
17
|
def response_values(env)
|
16
|
-
{:status => env
|
18
|
+
{:status => env.status, :headers => env.response_headers, :body => env.body}
|
17
19
|
end
|
18
20
|
end
|
19
21
|
end
|
data/lib/faraday/response.rb
CHANGED
@@ -13,43 +13,37 @@ module Faraday
|
|
13
13
|
# Override this to modify the environment after the response has finished.
|
14
14
|
# Calls the `parse` method if defined
|
15
15
|
def on_complete(env)
|
16
|
-
if respond_to?
|
17
|
-
env[:body] = parse(env[:body]) unless [204,304].index env[:status]
|
18
|
-
end
|
16
|
+
env.body = parse(env.body) if respond_to?(:parse) && env.parse_body?
|
19
17
|
end
|
20
18
|
end
|
21
19
|
|
22
20
|
extend Forwardable
|
23
|
-
extend AutoloadHelper
|
24
21
|
extend MiddlewareRegistry
|
25
22
|
|
26
|
-
|
27
|
-
:
|
28
|
-
:
|
29
|
-
|
30
|
-
register_middleware \
|
31
|
-
:raise_error => :RaiseError,
|
32
|
-
:logger => :Logger
|
23
|
+
register_middleware File.expand_path('../response', __FILE__),
|
24
|
+
:raise_error => [:RaiseError, 'raise_error'],
|
25
|
+
:logger => [:Logger, 'logger']
|
33
26
|
|
34
27
|
def initialize(env = nil)
|
35
|
-
@env = env
|
28
|
+
@env = Env.from(env) if env
|
36
29
|
@on_complete_callbacks = []
|
37
30
|
end
|
38
31
|
|
39
32
|
attr_reader :env
|
40
|
-
|
33
|
+
|
34
|
+
def_delegators :env, :to_hash
|
41
35
|
|
42
36
|
def status
|
43
|
-
finished? ? env
|
37
|
+
finished? ? env.status : nil
|
44
38
|
end
|
45
39
|
|
46
40
|
def headers
|
47
|
-
finished? ? env
|
41
|
+
finished? ? env.response_headers : {}
|
48
42
|
end
|
49
43
|
def_delegator :headers, :[]
|
50
44
|
|
51
45
|
def body
|
52
|
-
finished? ? env
|
46
|
+
finished? ? env.body : nil
|
53
47
|
end
|
54
48
|
|
55
49
|
def finished?
|
@@ -60,39 +54,39 @@ module Faraday
|
|
60
54
|
if not finished?
|
61
55
|
@on_complete_callbacks << Proc.new
|
62
56
|
else
|
63
|
-
yield
|
57
|
+
yield(env)
|
64
58
|
end
|
65
59
|
return self
|
66
60
|
end
|
67
61
|
|
68
62
|
def finish(env)
|
69
63
|
raise "response already finished" if finished?
|
70
|
-
@env = env
|
64
|
+
@env = Env.from(env)
|
71
65
|
@on_complete_callbacks.each { |callback| callback.call(env) }
|
72
66
|
return self
|
73
67
|
end
|
74
68
|
|
75
69
|
def success?
|
76
|
-
|
70
|
+
finished? && env.success?
|
77
71
|
end
|
78
72
|
|
79
73
|
# because @on_complete_callbacks cannot be marshalled
|
80
74
|
def marshal_dump
|
81
75
|
!finished? ? nil : {
|
82
|
-
:status => @env
|
83
|
-
:response_headers => @env
|
76
|
+
:status => @env.status, :body => @env.body,
|
77
|
+
:response_headers => @env.response_headers
|
84
78
|
}
|
85
79
|
end
|
86
80
|
|
87
81
|
def marshal_load(env)
|
88
|
-
@env = env
|
82
|
+
@env = Env.from(env)
|
89
83
|
end
|
90
84
|
|
91
85
|
# Expand the env with more properties, without overriding existing ones.
|
92
86
|
# Useful for applying request params after restoring a marshalled Response.
|
93
87
|
def apply_request(request_env)
|
94
88
|
raise "response didn't finish yet" unless finished?
|
95
|
-
@env = request_env.
|
89
|
+
@env = Env.from(request_env).update(@env)
|
96
90
|
return self
|
97
91
|
end
|
98
92
|
end
|