faraday 0.7.6 → 0.8.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -4
- data/README.md +148 -131
- data/Rakefile +12 -60
- data/faraday.gemspec +11 -32
- data/lib/faraday.rb +59 -12
- data/lib/faraday/adapter.rb +17 -1
- data/lib/faraday/adapter/em_http.rb +204 -0
- data/lib/faraday/adapter/em_synchrony.rb +66 -16
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
- data/lib/faraday/adapter/net_http.rb +1 -1
- data/lib/faraday/adapter/test.rb +13 -6
- data/lib/faraday/adapter/typhoeus.rb +64 -29
- data/lib/faraday/builder.rb +12 -17
- data/lib/faraday/connection.rb +131 -102
- data/lib/faraday/middleware.rb +17 -9
- data/lib/faraday/request.rb +40 -25
- data/lib/faraday/request/basic_authentication.rb +17 -0
- data/lib/faraday/request/retry.rb +21 -0
- data/lib/faraday/request/token_authentication.rb +21 -0
- data/lib/faraday/response.rb +2 -1
- data/lib/faraday/utils.rb +122 -18
- data/test/adapters/live_test.rb +35 -30
- data/test/adapters/logger_test.rb +2 -2
- data/test/adapters/typhoeus_test.rb +1 -1
- data/test/authentication_middleware_test.rb +48 -0
- data/test/connection_test.rb +171 -83
- data/test/env_test.rb +28 -3
- data/test/helper.rb +1 -1
- data/test/middleware/retry_test.rb +25 -0
- data/test/middleware_stack_test.rb +56 -2
- data/test/request_middleware_test.rb +2 -47
- data/test/response_middleware_test.rb +4 -4
- metadata +25 -39
- data/lib/faraday/request/json.rb +0 -35
data/lib/faraday/middleware.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
module Faraday
|
2
2
|
class Middleware
|
3
|
-
|
4
|
-
attr_accessor :load_error, :supports_parallel_requests
|
5
|
-
alias supports_parallel_requests? supports_parallel_requests
|
3
|
+
extend MiddlewareRegistry
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
nil
|
11
|
-
end
|
5
|
+
class << self
|
6
|
+
attr_accessor :load_error
|
7
|
+
private :load_error=
|
12
8
|
end
|
13
9
|
|
10
|
+
self.load_error = nil
|
11
|
+
|
14
12
|
# Executes a block which should try to require and reference dependent libraries
|
15
13
|
def self.dependency(lib = nil)
|
16
14
|
lib ? require(lib) : yield
|
@@ -18,8 +16,18 @@ module Faraday
|
|
18
16
|
self.load_error = error
|
19
17
|
end
|
20
18
|
|
19
|
+
def self.new(*)
|
20
|
+
raise "missing dependency for #{self}: #{load_error.message}" unless loaded?
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
21
24
|
def self.loaded?
|
22
|
-
|
25
|
+
load_error.nil?
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.inherited(subclass)
|
29
|
+
super
|
30
|
+
subclass.send(:load_error=, self.load_error)
|
23
31
|
end
|
24
32
|
|
25
33
|
def initialize(app = nil)
|
data/lib/faraday/request.rb
CHANGED
@@ -9,20 +9,24 @@ module Faraday
|
|
9
9
|
# req.body = 'abc'
|
10
10
|
# end
|
11
11
|
#
|
12
|
-
class Request < Struct.new(:path, :params, :headers, :body, :options)
|
12
|
+
class Request < Struct.new(:method, :path, :params, :headers, :body, :options)
|
13
13
|
extend AutoloadHelper
|
14
|
+
extend MiddlewareRegistry
|
14
15
|
|
15
16
|
autoload_all 'faraday/request',
|
16
|
-
:JSON => 'json',
|
17
17
|
:UrlEncoded => 'url_encoded',
|
18
|
-
:Multipart => 'multipart'
|
18
|
+
:Multipart => 'multipart',
|
19
|
+
:Retry => 'retry',
|
20
|
+
:Timeout => 'timeout',
|
21
|
+
:BasicAuthentication => 'basic_authentication',
|
22
|
+
:TokenAuthentication => 'token_authentication'
|
19
23
|
|
20
|
-
|
21
|
-
:json => :JSON,
|
24
|
+
register_middleware \
|
22
25
|
:url_encoded => :UrlEncoded,
|
23
|
-
:multipart => :Multipart
|
24
|
-
|
25
|
-
|
26
|
+
:multipart => :Multipart,
|
27
|
+
:retry => :Retry,
|
28
|
+
:basic_auth => :BasicAuthentication,
|
29
|
+
:token_auth => :TokenAuthentication
|
26
30
|
|
27
31
|
def self.create(request_method)
|
28
32
|
new(request_method).tap do |request|
|
@@ -30,16 +34,32 @@ module Faraday
|
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
# Public: Replace params, preserving the existing hash type
|
38
|
+
def params=(hash)
|
39
|
+
if params then params.replace hash
|
40
|
+
else super
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
44
|
+
# Public: Replace request headers, preserving the existing hash type
|
45
|
+
def headers=(hash)
|
46
|
+
if headers then headers.replace hash
|
47
|
+
else super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def url(path, params = nil)
|
52
|
+
if path.respond_to? :query
|
53
|
+
if query = path.query
|
54
|
+
path = path.dup
|
55
|
+
path.query = nil
|
56
|
+
end
|
57
|
+
else
|
58
|
+
path, query = path.split('?', 2)
|
59
|
+
end
|
60
|
+
self.path = path
|
61
|
+
self.params.merge_query query
|
62
|
+
self.params.update(params) if params
|
43
63
|
end
|
44
64
|
|
45
65
|
def [](key)
|
@@ -53,7 +73,7 @@ module Faraday
|
|
53
73
|
# ENV Keys
|
54
74
|
# :method - a symbolized request method (:get, :post)
|
55
75
|
# :body - the request body that will eventually be converted to a string.
|
56
|
-
# :url -
|
76
|
+
# :url - URI instance for the current request.
|
57
77
|
# :status - HTTP response status code
|
58
78
|
# :request_headers - hash of HTTP Headers to be sent to the server
|
59
79
|
# :response_headers - Hash of HTTP headers from the server
|
@@ -67,17 +87,12 @@ module Faraday
|
|
67
87
|
# :password - Proxy server password
|
68
88
|
# :ssl - Hash of options for configuring SSL requests.
|
69
89
|
def to_env(connection)
|
70
|
-
env_params = connection.params.merge(params)
|
71
|
-
env_headers = connection.headers.merge(headers)
|
72
|
-
request_options = Utils.deep_merge(connection.options, options)
|
73
|
-
Utils.deep_merge!(request_options, :proxy => connection.proxy)
|
74
|
-
|
75
90
|
{ :method => method,
|
76
91
|
:body => body,
|
77
|
-
:url => connection.
|
78
|
-
:request_headers =>
|
92
|
+
:url => connection.build_exclusive_url(path, params),
|
93
|
+
:request_headers => headers,
|
79
94
|
:parallel_manager => connection.parallel_manager,
|
80
|
-
:request =>
|
95
|
+
:request => options,
|
81
96
|
:ssl => connection.ssl}
|
82
97
|
end
|
83
98
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
class Request::BasicAuthentication < Faraday::Middleware
|
5
|
+
def initialize(app, login, pass)
|
6
|
+
super(app)
|
7
|
+
@header_value = "Basic #{Base64.encode64([login, pass].join(':')).gsub("\n", '')}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
unless env[:request_headers]['Authorization']
|
12
|
+
env[:request_headers]['Authorization'] = @header_value
|
13
|
+
end
|
14
|
+
@app.call(env)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::Retry < Faraday::Middleware
|
3
|
+
def initialize(app, retries = 2)
|
4
|
+
@retries = retries
|
5
|
+
super(app)
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
retries = @retries
|
10
|
+
begin
|
11
|
+
@app.call(env)
|
12
|
+
rescue StandardError, Timeout::Error
|
13
|
+
if retries > 0
|
14
|
+
retries -= 1
|
15
|
+
retry
|
16
|
+
end
|
17
|
+
raise
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Request::TokenAuthentication < Faraday::Middleware
|
3
|
+
def initialize(app, token, options={})
|
4
|
+
super(app)
|
5
|
+
|
6
|
+
values = ["token=#{token.to_s.inspect}"]
|
7
|
+
options.each do |key, value|
|
8
|
+
values << "#{key}=#{value.to_s.inspect}"
|
9
|
+
end
|
10
|
+
comma = ",\n#{' ' * ('Authorization: Token '.size)}"
|
11
|
+
@header_value = "Token #{values * comma}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
unless env[:request_headers]['Authorization']
|
16
|
+
env[:request_headers]['Authorization'] = @header_value
|
17
|
+
end
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/faraday/response.rb
CHANGED
@@ -21,12 +21,13 @@ module Faraday
|
|
21
21
|
|
22
22
|
extend Forwardable
|
23
23
|
extend AutoloadHelper
|
24
|
+
extend MiddlewareRegistry
|
24
25
|
|
25
26
|
autoload_all 'faraday/response',
|
26
27
|
:RaiseError => 'raise_error',
|
27
28
|
:Logger => 'logger'
|
28
29
|
|
29
|
-
|
30
|
+
register_middleware \
|
30
31
|
:raise_error => :RaiseError,
|
31
32
|
:logger => :Logger
|
32
33
|
|
data/lib/faraday/utils.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
-
require '
|
1
|
+
require 'cgi'
|
2
2
|
|
3
3
|
module Faraday
|
4
4
|
module Utils
|
5
|
-
include Rack::Utils
|
6
|
-
|
7
|
-
extend Rack::Utils
|
8
5
|
extend self
|
9
6
|
|
10
|
-
|
7
|
+
# Adapted from Rack::Utils::HeaderHash
|
8
|
+
class Headers < ::Hash
|
9
|
+
def initialize(hash={})
|
10
|
+
super()
|
11
|
+
@names = {}
|
12
|
+
self.update hash
|
13
|
+
end
|
14
|
+
|
11
15
|
# symbol -> string mapper + cache
|
12
16
|
KeyMap = Hash.new do |map, key|
|
13
17
|
map[key] = if key.respond_to?(:to_str) then key
|
@@ -20,17 +24,53 @@ module Faraday
|
|
20
24
|
KeyMap[:etag] = "ETag"
|
21
25
|
|
22
26
|
def [](k)
|
23
|
-
|
27
|
+
k = KeyMap[k]
|
28
|
+
super(k) || super(@names[k.downcase])
|
24
29
|
end
|
25
30
|
|
26
31
|
def []=(k, v)
|
32
|
+
k = KeyMap[k]
|
33
|
+
k = (@names[k.downcase] ||= k)
|
27
34
|
# join multiple values with a comma
|
28
35
|
v = v.to_ary.join(', ') if v.respond_to? :to_ary
|
29
|
-
super
|
36
|
+
super k, v
|
37
|
+
end
|
38
|
+
|
39
|
+
def delete(k)
|
40
|
+
k = KeyMap[k]
|
41
|
+
if k = @names[k.downcase]
|
42
|
+
@names.delete k.downcase
|
43
|
+
super k
|
44
|
+
end
|
30
45
|
end
|
31
46
|
|
47
|
+
def include?(k)
|
48
|
+
@names.include? k.downcase
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :has_key?, :include?
|
52
|
+
alias_method :member?, :include?
|
53
|
+
alias_method :key?, :include?
|
54
|
+
|
55
|
+
def merge!(other)
|
56
|
+
other.each { |k, v| self[k] = v }
|
57
|
+
self
|
58
|
+
end
|
32
59
|
alias_method :update, :merge!
|
33
60
|
|
61
|
+
def merge(other)
|
62
|
+
hash = dup
|
63
|
+
hash.merge! other
|
64
|
+
end
|
65
|
+
|
66
|
+
def replace(other)
|
67
|
+
clear
|
68
|
+
self.update other
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_hash() ::Hash.new.update(self) end
|
73
|
+
|
34
74
|
def parse(header_string)
|
35
75
|
return unless header_string && !header_string.empty?
|
36
76
|
header_string.split(/\r\n/).
|
@@ -92,7 +132,7 @@ module Faraday
|
|
92
132
|
end
|
93
133
|
|
94
134
|
def to_query
|
95
|
-
Utils.
|
135
|
+
Utils.build_nested_query(self)
|
96
136
|
end
|
97
137
|
|
98
138
|
private
|
@@ -102,10 +142,18 @@ module Faraday
|
|
102
142
|
end
|
103
143
|
end
|
104
144
|
|
105
|
-
#
|
106
|
-
|
145
|
+
# Copied from Rack
|
146
|
+
def build_query(params)
|
147
|
+
params.map { |k, v|
|
148
|
+
if v.class == Array
|
149
|
+
build_query(v.map { |x| [k, x] })
|
150
|
+
else
|
151
|
+
v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
|
152
|
+
end
|
153
|
+
}.join("&")
|
154
|
+
end
|
107
155
|
|
108
|
-
#
|
156
|
+
# Rack's version modified to handle non-String values
|
109
157
|
def build_nested_query(value, prefix = nil)
|
110
158
|
case value
|
111
159
|
when Array
|
@@ -122,14 +170,70 @@ module Faraday
|
|
122
170
|
end
|
123
171
|
end
|
124
172
|
|
125
|
-
|
126
|
-
|
127
|
-
def
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
173
|
+
def escape(s) CGI.escape s.to_s end
|
174
|
+
|
175
|
+
def unescape(s) CGI.unescape s.to_s end
|
176
|
+
|
177
|
+
DEFAULT_SEP = /[&;] */n
|
178
|
+
|
179
|
+
# Adapted from Rack
|
180
|
+
def parse_query(qs)
|
181
|
+
params = {}
|
182
|
+
|
183
|
+
(qs || '').split(DEFAULT_SEP).each do |p|
|
184
|
+
k, v = p.split('=', 2).map { |x| unescape(x) }
|
185
|
+
|
186
|
+
if cur = params[k]
|
187
|
+
if cur.class == Array then params[k] << v
|
188
|
+
else params[k] = [cur, v]
|
189
|
+
end
|
190
|
+
else
|
191
|
+
params[k] = v
|
192
|
+
end
|
193
|
+
end
|
194
|
+
params
|
195
|
+
end
|
196
|
+
|
197
|
+
def parse_nested_query(qs)
|
198
|
+
params = {}
|
199
|
+
|
200
|
+
(qs || '').split(DEFAULT_SEP).each do |p|
|
201
|
+
k, v = p.split('=', 2).map { |s| unescape(s) }
|
202
|
+
normalize_params(params, k, v)
|
132
203
|
end
|
204
|
+
params
|
205
|
+
end
|
206
|
+
|
207
|
+
# Stolen from Rack
|
208
|
+
def normalize_params(params, name, v = nil)
|
209
|
+
name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
|
210
|
+
k = $1 || ''
|
211
|
+
after = $' || ''
|
212
|
+
|
213
|
+
return if k.empty?
|
214
|
+
|
215
|
+
if after == ""
|
216
|
+
params[k] = v
|
217
|
+
elsif after == "[]"
|
218
|
+
params[k] ||= []
|
219
|
+
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
220
|
+
params[k] << v
|
221
|
+
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$)
|
222
|
+
child_key = $1
|
223
|
+
params[k] ||= []
|
224
|
+
raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
|
225
|
+
if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key)
|
226
|
+
normalize_params(params[k].last, child_key, v)
|
227
|
+
else
|
228
|
+
params[k] << normalize_params({}, child_key, v)
|
229
|
+
end
|
230
|
+
else
|
231
|
+
params[k] ||= {}
|
232
|
+
raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash)
|
233
|
+
params[k] = normalize_params(params[k], after, v)
|
234
|
+
end
|
235
|
+
|
236
|
+
return params
|
133
237
|
end
|
134
238
|
|
135
239
|
# Receives a URL and returns just the path with the query string sorted.
|
data/test/adapters/live_test.rb
CHANGED
@@ -6,10 +6,10 @@ else
|
|
6
6
|
module Adapters
|
7
7
|
class LiveTest < Faraday::TestCase
|
8
8
|
adapters = if ENV['ADAPTER']
|
9
|
-
ENV['ADAPTER'].split(':').map { |name| Faraday::Adapter.
|
9
|
+
ENV['ADAPTER'].split(':').map { |name| Faraday::Adapter.lookup_middleware name.to_sym }
|
10
10
|
else
|
11
11
|
loaded_adapters = Faraday::Adapter.all_loaded_constants
|
12
|
-
loaded_adapters -= [Faraday::Adapter::ActionDispatch]
|
12
|
+
loaded_adapters -= [Faraday::Adapter::Test, Faraday::Adapter::ActionDispatch]
|
13
13
|
# https://github.com/geemus/excon/issues/98
|
14
14
|
loaded_adapters -= [Faraday::Adapter::Excon] if defined? RUBY_ENGINE and "rbx" == RUBY_ENGINE
|
15
15
|
loaded_adapters << :default
|
@@ -108,18 +108,16 @@ else
|
|
108
108
|
end
|
109
109
|
|
110
110
|
# https://github.com/toland/patron/issues/34
|
111
|
-
unless %w[Faraday::Adapter::Patron
|
111
|
+
unless %w[Faraday::Adapter::Patron].include? adapter.to_s
|
112
112
|
define_method "test_#{adapter}_PATCH_send_url_encoded_params" do
|
113
113
|
resp = create_connection(adapter).patch('echo_name', 'name' => 'zack')
|
114
114
|
assert_equal %("zack"), resp.body
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
assert_equal "hi", resp.body
|
122
|
-
end
|
118
|
+
define_method "test_#{adapter}_OPTIONS" do
|
119
|
+
resp = create_connection(adapter).run_request(:options, '/options', nil, {})
|
120
|
+
assert_equal "hi", resp.body
|
123
121
|
end
|
124
122
|
|
125
123
|
define_method "test_#{adapter}_HEAD_send_url_encoded_params" do
|
@@ -145,32 +143,38 @@ else
|
|
145
143
|
assert_match(/deleted/, create_connection(adapter).delete('delete_with_json').body)
|
146
144
|
end
|
147
145
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
connection.get('hello_world')
|
152
|
-
assert !connection.in_parallel?
|
153
|
-
assert_equal 'hello world', connection.get('hello_world').body
|
154
|
-
end
|
155
|
-
|
156
|
-
define_method "test_#{adapter}_async_requests_uses_parallel_manager_to_run_multiple_json_requests" do
|
157
|
-
resp1, resp2 = nil, nil
|
146
|
+
if :default != adapter and adapter.supports_parallel?
|
147
|
+
define_method "test_#{adapter}_in_parallel" do
|
148
|
+
resp1, resp2 = nil, nil
|
158
149
|
|
159
|
-
|
160
|
-
|
150
|
+
connection = create_connection(adapter)
|
151
|
+
klass = real_adapter_for(adapter)
|
161
152
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
if adapter.supports_parallel_requests?
|
153
|
+
connection.in_parallel do
|
154
|
+
resp1 = connection.get('echo?a=1')
|
155
|
+
resp2 = connection.get('echo?b=2')
|
166
156
|
assert connection.in_parallel?
|
167
157
|
assert_nil resp1.body
|
168
158
|
assert_nil resp2.body
|
169
159
|
end
|
160
|
+
assert !connection.in_parallel?
|
161
|
+
assert_equal 'get ?{"a"=>"1"}', resp1.body
|
162
|
+
assert_equal 'get ?{"b"=>"2"}', resp2.body
|
163
|
+
end
|
164
|
+
else
|
165
|
+
define_method "test_#{adapter}_no_parallel_support" do
|
166
|
+
connection = create_connection(adapter)
|
167
|
+
response = nil
|
168
|
+
|
169
|
+
err = capture_warnings do
|
170
|
+
connection.in_parallel do
|
171
|
+
response = connection.get('echo').body
|
172
|
+
end
|
173
|
+
end
|
174
|
+
assert response
|
175
|
+
assert_match "no parallel-capable adapter on Faraday stack", err
|
176
|
+
assert_match __FILE__, err
|
170
177
|
end
|
171
|
-
assert !connection.in_parallel?
|
172
|
-
assert_equal '[1,2,3]', resp1.body
|
173
|
-
assert_equal '[1,2,3]', resp2.body
|
174
178
|
end
|
175
179
|
|
176
180
|
if adapter.to_s == "Faraday::Adapter::EMSynchrony"
|
@@ -187,9 +191,10 @@ else
|
|
187
191
|
end
|
188
192
|
end
|
189
193
|
|
190
|
-
|
194
|
+
# https://github.com/eventmachine/eventmachine/pull/289
|
195
|
+
unless %w[Faraday::Adapter::EMHttp Faraday::Adapter::EMSynchrony Faraday::Adapter::Excon].include?(adapter.to_s)
|
191
196
|
define_method "test_#{adapter}_timeout" do
|
192
|
-
conn = create_connection(adapter, :request => {:timeout => 1, :
|
197
|
+
conn = create_connection(adapter, :request => {:timeout => 1, :open_timeout => 1})
|
193
198
|
assert_raise Faraday::Error::TimeoutError do
|
194
199
|
conn.get '/slow'
|
195
200
|
end
|
@@ -217,7 +222,7 @@ else
|
|
217
222
|
|
218
223
|
def real_adapter_for(adapter)
|
219
224
|
if adapter == :default
|
220
|
-
Faraday::Adapter.
|
225
|
+
Faraday::Adapter.lookup_middleware(Faraday.default_adapter)
|
221
226
|
else
|
222
227
|
adapter
|
223
228
|
end
|