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.
- checksums.yaml +7 -0
- data/.document +6 -0
- data/CHANGELOG.md +15 -0
- data/CONTRIBUTING.md +36 -0
- data/Gemfile +25 -15
- data/{LICENSE → LICENSE.md} +1 -1
- data/README.md +187 -137
- data/Rakefile +38 -101
- data/faraday.gemspec +26 -81
- data/lib/faraday/adapter/em_http.rb +237 -0
- data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
- data/lib/faraday/adapter/em_synchrony.rb +65 -38
- data/lib/faraday/adapter/excon.rb +60 -9
- data/lib/faraday/adapter/httpclient.rb +106 -0
- data/lib/faraday/adapter/net_http.rb +89 -38
- data/lib/faraday/adapter/net_http_persistent.rb +47 -0
- data/lib/faraday/adapter/patron.rb +46 -9
- data/lib/faraday/adapter/rack.rb +58 -0
- data/lib/faraday/adapter/test.rb +68 -28
- data/lib/faraday/adapter/typhoeus.rb +94 -16
- data/lib/faraday/adapter.rb +33 -25
- data/lib/faraday/autoload.rb +85 -0
- data/lib/faraday/connection.rb +342 -134
- data/lib/faraday/error.rb +44 -29
- data/lib/faraday/middleware.rb +18 -10
- data/lib/faraday/options.rb +350 -0
- data/lib/faraday/parameters.rb +193 -0
- data/lib/faraday/rack_builder.rb +212 -0
- data/lib/faraday/request/authorization.rb +42 -0
- data/lib/faraday/request/basic_authentication.rb +13 -0
- data/lib/faraday/request/instrumentation.rb +36 -0
- data/lib/faraday/request/multipart.rb +15 -15
- data/lib/faraday/request/retry.rb +118 -0
- data/lib/faraday/request/token_authentication.rb +15 -0
- data/lib/faraday/request/url_encoded.rb +8 -9
- data/lib/faraday/request.rb +46 -45
- data/lib/faraday/response/logger.rb +4 -4
- data/lib/faraday/response/raise_error.rb +8 -3
- data/lib/faraday/response.rb +20 -25
- data/lib/faraday/upload_io.rb +51 -7
- data/lib/faraday/utils.rb +240 -44
- data/lib/faraday.rb +218 -38
- data/script/console +7 -0
- data/script/generate_certs +42 -0
- data/script/package +7 -0
- data/script/proxy-server +42 -0
- data/script/release +17 -0
- data/script/server +36 -0
- data/script/test +172 -0
- data/test/adapters/default_test.rb +14 -0
- data/test/adapters/em_http_test.rb +20 -0
- data/test/adapters/em_synchrony_test.rb +20 -0
- data/test/adapters/excon_test.rb +20 -0
- data/test/adapters/httpclient_test.rb +21 -0
- data/test/adapters/integration.rb +254 -0
- data/test/adapters/logger_test.rb +3 -3
- data/test/adapters/net_http_persistent_test.rb +20 -0
- data/test/adapters/net_http_test.rb +6 -25
- data/test/adapters/patron_test.rb +20 -0
- data/test/adapters/rack_test.rb +31 -0
- data/test/adapters/test_middleware_test.rb +74 -3
- data/test/adapters/typhoeus_test.rb +28 -0
- data/test/authentication_middleware_test.rb +65 -0
- data/test/composite_read_io_test.rb +111 -0
- data/test/connection_test.rb +381 -104
- data/test/env_test.rb +116 -40
- data/test/helper.rb +61 -25
- data/test/live_server.rb +55 -29
- data/test/middleware/instrumentation_test.rb +88 -0
- data/test/middleware/retry_test.rb +109 -0
- data/test/middleware_stack_test.rb +87 -5
- data/test/multibyte.txt +1 -0
- data/test/options_test.rb +252 -0
- data/test/request_middleware_test.rb +78 -21
- data/test/response_middleware_test.rb +32 -7
- data/test/strawberry.rb +2 -0
- data/test/utils_test.rb +58 -0
- metadata +116 -117
- data/lib/faraday/adapter/action_dispatch.rb +0 -30
- data/lib/faraday/builder.rb +0 -137
- data/lib/faraday/request/json.rb +0 -31
- data/test/adapters/live_test.rb +0 -186
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
module Faraday
|
|
2
|
+
# A Builder that processes requests into responses by passing through an inner
|
|
3
|
+
# middleware stack (heavily inspired by Rack).
|
|
4
|
+
#
|
|
5
|
+
# Faraday::Connection.new(:url => 'http://sushi.com') do |builder|
|
|
6
|
+
# builder.request :url_encoded # Faraday::Request::UrlEncoded
|
|
7
|
+
# builder.adapter :net_http # Faraday::Adapter::NetHttp
|
|
8
|
+
# end
|
|
9
|
+
class RackBuilder
|
|
10
|
+
attr_accessor :handlers
|
|
11
|
+
|
|
12
|
+
# Error raised when trying to modify the stack after calling `lock!`
|
|
13
|
+
class StackLocked < RuntimeError; end
|
|
14
|
+
|
|
15
|
+
# borrowed from ActiveSupport::Dependencies::Reference &
|
|
16
|
+
# ActionDispatch::MiddlewareStack::Middleware
|
|
17
|
+
class Handler
|
|
18
|
+
@@constants_mutex = Mutex.new
|
|
19
|
+
@@constants = Hash.new { |h, k|
|
|
20
|
+
value = k.respond_to?(:constantize) ? k.constantize : Object.const_get(k)
|
|
21
|
+
@@constants_mutex.synchronize { h[k] = value }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
attr_reader :name
|
|
25
|
+
|
|
26
|
+
def initialize(klass, *args, &block)
|
|
27
|
+
@name = klass.to_s
|
|
28
|
+
if klass.respond_to?(:name)
|
|
29
|
+
@@constants_mutex.synchronize { @@constants[@name] = klass }
|
|
30
|
+
end
|
|
31
|
+
@args, @block = args, block
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def klass() @@constants[@name] end
|
|
35
|
+
def inspect() @name end
|
|
36
|
+
|
|
37
|
+
def ==(other)
|
|
38
|
+
if other.is_a? Handler
|
|
39
|
+
self.name == other.name
|
|
40
|
+
elsif other.respond_to? :name
|
|
41
|
+
klass == other
|
|
42
|
+
else
|
|
43
|
+
@name == other.to_s
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def build(app)
|
|
48
|
+
klass.new(app, *@args, &@block)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def initialize(handlers = [])
|
|
53
|
+
@handlers = handlers
|
|
54
|
+
if block_given?
|
|
55
|
+
build(&Proc.new)
|
|
56
|
+
elsif @handlers.empty?
|
|
57
|
+
# default stack, if nothing else is configured
|
|
58
|
+
self.request :url_encoded
|
|
59
|
+
self.adapter Faraday.default_adapter
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def build(options = {})
|
|
64
|
+
raise_if_locked
|
|
65
|
+
@handlers.clear unless options[:keep]
|
|
66
|
+
yield(self) if block_given?
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def [](idx)
|
|
70
|
+
@handlers[idx]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Locks the middleware stack to ensure no further modifications are possible.
|
|
74
|
+
def lock!
|
|
75
|
+
@handlers.freeze
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def locked?
|
|
79
|
+
@handlers.frozen?
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def use(klass, *args, &block)
|
|
83
|
+
if klass.is_a? Symbol
|
|
84
|
+
use_symbol(Faraday::Middleware, klass, *args, &block)
|
|
85
|
+
else
|
|
86
|
+
raise_if_locked
|
|
87
|
+
@handlers << self.class::Handler.new(klass, *args, &block)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def request(key, *args, &block)
|
|
92
|
+
use_symbol(Faraday::Request, key, *args, &block)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def response(key, *args, &block)
|
|
96
|
+
use_symbol(Faraday::Response, key, *args, &block)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def adapter(key, *args, &block)
|
|
100
|
+
use_symbol(Faraday::Adapter, key, *args, &block)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
## methods to push onto the various positions in the stack:
|
|
104
|
+
|
|
105
|
+
def insert(index, *args, &block)
|
|
106
|
+
raise_if_locked
|
|
107
|
+
index = assert_index(index)
|
|
108
|
+
handler = self.class::Handler.new(*args, &block)
|
|
109
|
+
@handlers.insert(index, handler)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
alias_method :insert_before, :insert
|
|
113
|
+
|
|
114
|
+
def insert_after(index, *args, &block)
|
|
115
|
+
index = assert_index(index)
|
|
116
|
+
insert(index + 1, *args, &block)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def swap(index, *args, &block)
|
|
120
|
+
raise_if_locked
|
|
121
|
+
index = assert_index(index)
|
|
122
|
+
@handlers.delete_at(index)
|
|
123
|
+
insert(index, *args, &block)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def delete(handler)
|
|
127
|
+
raise_if_locked
|
|
128
|
+
@handlers.delete(handler)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Processes a Request into a Response by passing it through this Builder's
|
|
132
|
+
# middleware stack.
|
|
133
|
+
#
|
|
134
|
+
# connection - Faraday::Connection
|
|
135
|
+
# request - Faraday::Request
|
|
136
|
+
#
|
|
137
|
+
# Returns a Faraday::Response.
|
|
138
|
+
def build_response(connection, request)
|
|
139
|
+
app.call(build_env(connection, request))
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# The "rack app" wrapped in middleware. All requests are sent here.
|
|
143
|
+
#
|
|
144
|
+
# The builder is responsible for creating the app object. After this,
|
|
145
|
+
# the builder gets locked to ensure no further modifications are made
|
|
146
|
+
# to the middleware stack.
|
|
147
|
+
#
|
|
148
|
+
# Returns an object that responds to `call` and returns a Response.
|
|
149
|
+
def app
|
|
150
|
+
@app ||= begin
|
|
151
|
+
lock!
|
|
152
|
+
to_app(lambda { |env|
|
|
153
|
+
response = Response.new
|
|
154
|
+
response.finish(env) unless env.parallel?
|
|
155
|
+
env.response = response
|
|
156
|
+
})
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def to_app(inner_app)
|
|
161
|
+
# last added handler is the deepest and thus closest to the inner app
|
|
162
|
+
@handlers.reverse.inject(inner_app) { |app, handler| handler.build(app) }
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def ==(other)
|
|
166
|
+
other.is_a?(self.class) && @handlers == other.handlers
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def dup
|
|
170
|
+
self.class.new(@handlers.dup)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# ENV Keys
|
|
174
|
+
# :method - a symbolized request method (:get, :post)
|
|
175
|
+
# :body - the request body that will eventually be converted to a string.
|
|
176
|
+
# :url - URI instance for the current request.
|
|
177
|
+
# :status - HTTP response status code
|
|
178
|
+
# :request_headers - hash of HTTP Headers to be sent to the server
|
|
179
|
+
# :response_headers - Hash of HTTP headers from the server
|
|
180
|
+
# :parallel_manager - sent if the connection is in parallel mode
|
|
181
|
+
# :request - Hash of options for configuring the request.
|
|
182
|
+
# :timeout - open/read timeout Integer in seconds
|
|
183
|
+
# :open_timeout - read timeout Integer in seconds
|
|
184
|
+
# :proxy - Hash of proxy options
|
|
185
|
+
# :uri - Proxy Server URI
|
|
186
|
+
# :user - Proxy server username
|
|
187
|
+
# :password - Proxy server password
|
|
188
|
+
# :ssl - Hash of options for configuring SSL requests.
|
|
189
|
+
def build_env(connection, request)
|
|
190
|
+
Env.new(request.method, request.body,
|
|
191
|
+
connection.build_exclusive_url(request.path, request.params),
|
|
192
|
+
request.options, request.headers, connection.ssl,
|
|
193
|
+
connection.parallel_manager)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
private
|
|
197
|
+
|
|
198
|
+
def raise_if_locked
|
|
199
|
+
raise StackLocked, "can't modify middleware stack after making a request" if locked?
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def use_symbol(mod, key, *args, &block)
|
|
203
|
+
use(mod.lookup_middleware(key), *args, &block)
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def assert_index(index)
|
|
207
|
+
idx = index.is_a?(Integer) ? index : @handlers.index(index)
|
|
208
|
+
raise "No such handler: #{index.inspect}" unless idx
|
|
209
|
+
idx
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Faraday
|
|
2
|
+
class Request::Authorization < Faraday::Middleware
|
|
3
|
+
KEY = "Authorization".freeze unless defined? KEY
|
|
4
|
+
|
|
5
|
+
# Public
|
|
6
|
+
def self.header(type, token)
|
|
7
|
+
case token
|
|
8
|
+
when String, Symbol
|
|
9
|
+
"#{type} #{token}"
|
|
10
|
+
when Hash
|
|
11
|
+
build_hash(type.to_s, token)
|
|
12
|
+
else
|
|
13
|
+
raise ArgumentError, "Can't build an Authorization #{type} header from #{token.inspect}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Internal
|
|
18
|
+
def self.build_hash(type, hash)
|
|
19
|
+
offset = KEY.size + type.size + 3
|
|
20
|
+
comma = ",\n#{' ' * offset}"
|
|
21
|
+
values = []
|
|
22
|
+
hash.each do |key, value|
|
|
23
|
+
values << "#{key}=#{value.to_s.inspect}"
|
|
24
|
+
end
|
|
25
|
+
"#{type} #{values * comma}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(app, type, token)
|
|
29
|
+
@header_value = self.class.header(type, token)
|
|
30
|
+
super(app)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Public
|
|
34
|
+
def call(env)
|
|
35
|
+
unless env.request_headers[KEY]
|
|
36
|
+
env.request_headers[KEY] = @header_value
|
|
37
|
+
end
|
|
38
|
+
@app.call(env)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module Faraday
|
|
4
|
+
class Request::BasicAuthentication < Request.load_middleware(:authorization)
|
|
5
|
+
# Public
|
|
6
|
+
def self.header(login, pass)
|
|
7
|
+
value = Base64.encode64([login, pass].join(':'))
|
|
8
|
+
value.gsub!("\n", '')
|
|
9
|
+
super(:Basic, value)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
@@ -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,46 +1,46 @@
|
|
|
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
|
|
23
24
|
|
|
24
|
-
def has_multipart?(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return true if has_multipart?(val)
|
|
25
|
+
def has_multipart?(obj)
|
|
26
|
+
# string is an enum in 1.8, returning list of itself
|
|
27
|
+
if obj.respond_to?(:each) && !obj.is_a?(String)
|
|
28
|
+
(obj.respond_to?(:values) ? obj.values : obj).each do |val|
|
|
29
|
+
return true if (val.respond_to?(:content_type) || has_multipart?(val))
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
false
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def create_multipart(env, params)
|
|
36
|
-
boundary = env
|
|
36
|
+
boundary = env.request.boundary
|
|
37
37
|
parts = process_params(params) do |key, value|
|
|
38
38
|
Faraday::Parts::Part.new(boundary, key, value)
|
|
39
39
|
end
|
|
40
40
|
parts << Faraday::Parts::EpiloguePart.new(boundary)
|
|
41
41
|
|
|
42
42
|
body = Faraday::CompositeReadIO.new(parts)
|
|
43
|
-
env
|
|
43
|
+
env.request_headers[Faraday::Env::ContentLength] = body.length.to_s
|
|
44
44
|
return body
|
|
45
45
|
end
|
|
46
46
|
|
|
@@ -0,0 +1,118 @@
|
|
|
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
|
+
#
|
|
21
|
+
class Request::Retry < Faraday::Middleware
|
|
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)
|
|
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
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def call(env)
|
|
83
|
+
retries = @options.max
|
|
84
|
+
request_body = env[:body]
|
|
85
|
+
begin
|
|
86
|
+
env[:body] = request_body # after failure env[:body] is set to the response body
|
|
87
|
+
@app.call(env)
|
|
88
|
+
rescue @errmatch
|
|
89
|
+
if retries > 0
|
|
90
|
+
retries -= 1
|
|
91
|
+
sleep sleep_amount(retries + 1)
|
|
92
|
+
retry
|
|
93
|
+
end
|
|
94
|
+
raise
|
|
95
|
+
end
|
|
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
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Faraday
|
|
2
|
+
class Request::TokenAuthentication < Request.load_middleware(:authorization)
|
|
3
|
+
# Public
|
|
4
|
+
def self.header(token, options = nil)
|
|
5
|
+
options ||= {}
|
|
6
|
+
options[:token] = token
|
|
7
|
+
super(:Token, options)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(app, token, options = nil)
|
|
11
|
+
super(app, token, options)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
@@ -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,27 +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
|
-
type = request_type(env)
|
|
19
|
-
|
|
20
19
|
if process_request?(env)
|
|
21
|
-
env
|
|
22
|
-
yield
|
|
20
|
+
env.request_headers[CONTENT_TYPE] ||= self.class.mime_type
|
|
21
|
+
yield(env.body) unless env.body.respond_to?(:to_str)
|
|
23
22
|
end
|
|
24
23
|
end
|
|
25
24
|
|
|
26
25
|
def process_request?(env)
|
|
27
26
|
type = request_type(env)
|
|
28
|
-
env
|
|
27
|
+
env.body and (type.empty? or type == self.class.mime_type)
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
def request_type(env)
|
|
32
|
-
type = env
|
|
31
|
+
type = env.request_headers[CONTENT_TYPE].to_s
|
|
33
32
|
type = type.split(';', 2).first if type.index(';')
|
|
34
33
|
type
|
|
35
34
|
end
|
data/lib/faraday/request.rb
CHANGED
|
@@ -9,34 +9,54 @@ module Faraday
|
|
|
9
9
|
# req.body = 'abc'
|
|
10
10
|
# end
|
|
11
11
|
#
|
|
12
|
-
class Request < Struct.new(:path, :params, :headers, :body)
|
|
13
|
-
extend
|
|
12
|
+
class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
|
|
13
|
+
extend MiddlewareRegistry
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
:
|
|
17
|
-
:
|
|
18
|
-
:
|
|
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']
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
def self.create(request_method)
|
|
25
|
+
new(request_method).tap do |request|
|
|
26
|
+
yield(request) if block_given?
|
|
27
|
+
end
|
|
28
|
+
end
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
# Public: Replace params, preserving the existing hash type
|
|
31
|
+
def params=(hash)
|
|
32
|
+
if params
|
|
33
|
+
params.replace hash
|
|
34
|
+
else
|
|
35
|
+
super
|
|
36
|
+
end
|
|
29
37
|
end
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
# Public: Replace request headers, preserving the existing hash type
|
|
40
|
+
def headers=(hash)
|
|
41
|
+
if headers
|
|
42
|
+
headers.replace hash
|
|
43
|
+
else
|
|
44
|
+
super
|
|
45
|
+
end
|
|
35
46
|
end
|
|
36
47
|
|
|
37
|
-
def url(path, params =
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
def url(path, params = nil)
|
|
49
|
+
if path.respond_to? :query
|
|
50
|
+
if query = path.query
|
|
51
|
+
path = path.dup
|
|
52
|
+
path.query = nil
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
path, query = path.split('?', 2)
|
|
56
|
+
end
|
|
57
|
+
self.path = path
|
|
58
|
+
self.params.merge_query query, options.params_encoder
|
|
59
|
+
self.params.update(params) if params
|
|
40
60
|
end
|
|
41
61
|
|
|
42
62
|
def [](key)
|
|
@@ -50,7 +70,7 @@ module Faraday
|
|
|
50
70
|
# ENV Keys
|
|
51
71
|
# :method - a symbolized request method (:get, :post)
|
|
52
72
|
# :body - the request body that will eventually be converted to a string.
|
|
53
|
-
# :url -
|
|
73
|
+
# :url - URI instance for the current request.
|
|
54
74
|
# :status - HTTP response status code
|
|
55
75
|
# :request_headers - hash of HTTP Headers to be sent to the server
|
|
56
76
|
# :response_headers - Hash of HTTP headers from the server
|
|
@@ -63,29 +83,10 @@ module Faraday
|
|
|
63
83
|
# :user - Proxy server username
|
|
64
84
|
# :password - Proxy server password
|
|
65
85
|
# :ssl - Hash of options for configuring SSL requests.
|
|
66
|
-
def
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
connection.merge_headers(env_headers, headers)
|
|
70
|
-
connection.merge_params(env_params, params)
|
|
71
|
-
|
|
72
|
-
{ :method => request_method,
|
|
73
|
-
:body => body,
|
|
74
|
-
:url => connection.build_url(path, env_params),
|
|
75
|
-
:request_headers => env_headers,
|
|
76
|
-
:parallel_manager => connection.parallel_manager,
|
|
77
|
-
:request => connection.options.merge(:proxy => connection.proxy),
|
|
78
|
-
:ssl => connection.ssl}
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def run(connection, request_method)
|
|
82
|
-
app = lambda { |env|
|
|
83
|
-
response = Response.new
|
|
84
|
-
response.finish(env) unless env[:parallel_manager]
|
|
85
|
-
env[:response] = response
|
|
86
|
-
}
|
|
87
|
-
env = to_env_hash(connection, request_method)
|
|
88
|
-
connection.builder.to_app(app).call(env)
|
|
86
|
+
def to_env(connection)
|
|
87
|
+
Env.new(method, body, connection.build_exclusive_url(path, params),
|
|
88
|
+
options, headers, connection.ssl, connection.parallel_manager)
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
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
|