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,350 @@
|
|
1
|
+
module Faraday
|
2
|
+
# Subclasses Struct with some special helpers for converting from a Hash to
|
3
|
+
# a Struct.
|
4
|
+
class Options < Struct
|
5
|
+
# Public
|
6
|
+
def self.from(value)
|
7
|
+
value ? new.update(value) : new
|
8
|
+
end
|
9
|
+
|
10
|
+
# Public
|
11
|
+
def each
|
12
|
+
return to_enum(:each) unless block_given?
|
13
|
+
members.each do |key|
|
14
|
+
yield(key.to_sym, send(key))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public
|
19
|
+
def update(obj)
|
20
|
+
obj.each do |key, value|
|
21
|
+
if sub_options = self.class.options_for(key)
|
22
|
+
value = sub_options.from(value) if value
|
23
|
+
elsif Hash === value
|
24
|
+
hash = {}
|
25
|
+
value.each do |hash_key, hash_value|
|
26
|
+
hash[hash_key] = hash_value
|
27
|
+
end
|
28
|
+
value = hash
|
29
|
+
end
|
30
|
+
|
31
|
+
self.send("#{key}=", value) unless value.nil?
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
alias merge! update
|
37
|
+
|
38
|
+
# Public
|
39
|
+
def delete(key)
|
40
|
+
value = send(key)
|
41
|
+
send("#{key}=", nil)
|
42
|
+
value
|
43
|
+
end
|
44
|
+
|
45
|
+
# Public
|
46
|
+
def clear
|
47
|
+
members.each { |member| delete(member) }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Public
|
51
|
+
def merge(value)
|
52
|
+
dup.update(value)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Public
|
56
|
+
def fetch(key, *args)
|
57
|
+
unless symbolized_key_set.include?(key.to_sym)
|
58
|
+
key_setter = "#{key}="
|
59
|
+
if args.size > 0
|
60
|
+
send(key_setter, args.first)
|
61
|
+
elsif block_given?
|
62
|
+
send(key_setter, Proc.new.call(key))
|
63
|
+
else
|
64
|
+
raise self.class.fetch_error_class, "key not found: #{key.inspect}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
send(key)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Public
|
71
|
+
def values_at(*keys)
|
72
|
+
keys.map { |key| send(key) }
|
73
|
+
end
|
74
|
+
|
75
|
+
# Public
|
76
|
+
def keys
|
77
|
+
members.reject { |member| send(member).nil? }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Public
|
81
|
+
def empty?
|
82
|
+
keys.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
# Public
|
86
|
+
def each_key
|
87
|
+
return to_enum(:each_key) unless block_given?
|
88
|
+
keys.each do |key|
|
89
|
+
yield(key)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Public
|
94
|
+
def key?(key)
|
95
|
+
keys.include?(key)
|
96
|
+
end
|
97
|
+
|
98
|
+
alias has_key? key?
|
99
|
+
|
100
|
+
# Public
|
101
|
+
def each_value
|
102
|
+
return to_enum(:each_value) unless block_given?
|
103
|
+
values.each do |value|
|
104
|
+
yield(value)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Public
|
109
|
+
def value?(value)
|
110
|
+
values.include?(value)
|
111
|
+
end
|
112
|
+
|
113
|
+
alias has_value? value?
|
114
|
+
|
115
|
+
# Public
|
116
|
+
def to_hash
|
117
|
+
hash = {}
|
118
|
+
members.each do |key|
|
119
|
+
value = send(key)
|
120
|
+
hash[key.to_sym] = value unless value.nil?
|
121
|
+
end
|
122
|
+
hash
|
123
|
+
end
|
124
|
+
|
125
|
+
# Internal
|
126
|
+
def inspect
|
127
|
+
values = []
|
128
|
+
members.each do |member|
|
129
|
+
value = send(member)
|
130
|
+
values << "#{member}=#{value.inspect}" if value
|
131
|
+
end
|
132
|
+
values = values.empty? ? ' (empty)' : (' ' << values.join(", "))
|
133
|
+
|
134
|
+
%(#<#{self.class}#{values}>)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Internal
|
138
|
+
def self.options(mapping)
|
139
|
+
attribute_options.update(mapping)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Internal
|
143
|
+
def self.options_for(key)
|
144
|
+
attribute_options[key]
|
145
|
+
end
|
146
|
+
|
147
|
+
# Internal
|
148
|
+
def self.attribute_options
|
149
|
+
@attribute_options ||= {}
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.memoized(key)
|
153
|
+
memoized_attributes[key.to_sym] = Proc.new
|
154
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
155
|
+
def #{key}() self[:#{key}]; end
|
156
|
+
RUBY
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.memoized_attributes
|
160
|
+
@memoized_attributes ||= {}
|
161
|
+
end
|
162
|
+
|
163
|
+
def [](key)
|
164
|
+
key = key.to_sym
|
165
|
+
if method = self.class.memoized_attributes[key]
|
166
|
+
super(key) || (self[key] = instance_eval(&method))
|
167
|
+
else
|
168
|
+
super
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def symbolized_key_set
|
173
|
+
@symbolized_key_set ||= Set.new(keys.map { |k| k.to_sym })
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.inherited(subclass)
|
177
|
+
super
|
178
|
+
subclass.attribute_options.update(attribute_options)
|
179
|
+
subclass.memoized_attributes.update(memoized_attributes)
|
180
|
+
end
|
181
|
+
|
182
|
+
def self.fetch_error_class
|
183
|
+
@fetch_error_class ||= if Object.const_defined?(:KeyError)
|
184
|
+
::KeyError
|
185
|
+
else
|
186
|
+
::IndexError
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
|
192
|
+
:timeout, :open_timeout, :boundary,
|
193
|
+
:oauth)
|
194
|
+
|
195
|
+
def []=(key, value)
|
196
|
+
if key && key.to_sym == :proxy
|
197
|
+
super(key, value ? ProxyOptions.from(value) : nil)
|
198
|
+
else
|
199
|
+
super(key, value)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
class SSLOptions < Options.new(:verify, :ca_file, :ca_path, :verify_mode,
|
205
|
+
:cert_store, :client_cert, :client_key, :certificate, :private_key, :verify_depth, :version)
|
206
|
+
|
207
|
+
def verify?
|
208
|
+
verify != false
|
209
|
+
end
|
210
|
+
|
211
|
+
def disable?
|
212
|
+
!verify?
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
class ProxyOptions < Options.new(:uri, :user, :password)
|
217
|
+
extend Forwardable
|
218
|
+
def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=, :path, :path=
|
219
|
+
|
220
|
+
def self.from(value)
|
221
|
+
case value
|
222
|
+
when String
|
223
|
+
value = {:uri => Utils.URI(value)}
|
224
|
+
when URI
|
225
|
+
value = {:uri => value}
|
226
|
+
when Hash, Options
|
227
|
+
if uri = value.delete(:uri)
|
228
|
+
value[:uri] = Utils.URI(uri)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
super(value)
|
232
|
+
end
|
233
|
+
|
234
|
+
memoized(:user) { uri.user && Utils.unescape(uri.user) }
|
235
|
+
memoized(:password) { uri.password && Utils.unescape(uri.password) }
|
236
|
+
end
|
237
|
+
|
238
|
+
class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
|
239
|
+
:parallel_manager, :params, :headers, :builder_class)
|
240
|
+
|
241
|
+
options :request => RequestOptions, :ssl => SSLOptions
|
242
|
+
|
243
|
+
memoized(:request) { self.class.options_for(:request).new }
|
244
|
+
|
245
|
+
memoized(:ssl) { self.class.options_for(:ssl).new }
|
246
|
+
|
247
|
+
memoized(:builder_class) { RackBuilder }
|
248
|
+
|
249
|
+
def new_builder(block)
|
250
|
+
builder_class.new(&block)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class Env < Options.new(:method, :body, :url, :request, :request_headers,
|
255
|
+
:ssl, :parallel_manager, :params, :response, :response_headers, :status)
|
256
|
+
|
257
|
+
ContentLength = 'Content-Length'.freeze
|
258
|
+
StatusesWithoutBody = Set.new [204, 304]
|
259
|
+
SuccessfulStatuses = 200..299
|
260
|
+
|
261
|
+
# A Set of HTTP verbs that typically send a body. If no body is set for
|
262
|
+
# these requests, the Content-Length header is set to 0.
|
263
|
+
MethodsWithBodies = Set.new [:post, :put, :patch, :options]
|
264
|
+
|
265
|
+
options :request => RequestOptions,
|
266
|
+
:request_headers => Utils::Headers, :response_headers => Utils::Headers
|
267
|
+
|
268
|
+
extend Forwardable
|
269
|
+
|
270
|
+
def_delegators :request, :params_encoder
|
271
|
+
|
272
|
+
# Public
|
273
|
+
def [](key)
|
274
|
+
if in_member_set?(key)
|
275
|
+
super(key)
|
276
|
+
else
|
277
|
+
custom_members[key]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Public
|
282
|
+
def []=(key, value)
|
283
|
+
if in_member_set?(key)
|
284
|
+
super(key, value)
|
285
|
+
else
|
286
|
+
custom_members[key] = value
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
# Public
|
291
|
+
def success?
|
292
|
+
SuccessfulStatuses.include?(status)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Public
|
296
|
+
def needs_body?
|
297
|
+
!body && MethodsWithBodies.include?(method)
|
298
|
+
end
|
299
|
+
|
300
|
+
# Public
|
301
|
+
def clear_body
|
302
|
+
request_headers[ContentLength] = '0'
|
303
|
+
self.body = ''
|
304
|
+
end
|
305
|
+
|
306
|
+
# Public
|
307
|
+
def parse_body?
|
308
|
+
!StatusesWithoutBody.include?(status)
|
309
|
+
end
|
310
|
+
|
311
|
+
# Public
|
312
|
+
def parallel?
|
313
|
+
!!parallel_manager
|
314
|
+
end
|
315
|
+
|
316
|
+
def inspect
|
317
|
+
attrs = [nil]
|
318
|
+
members.each do |mem|
|
319
|
+
if value = send(mem)
|
320
|
+
attrs << "@#{mem}=#{value.inspect}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
if !custom_members.empty?
|
324
|
+
attrs << "@custom=#{custom_members.inspect}"
|
325
|
+
end
|
326
|
+
%(#<#{self.class}#{attrs.join(" ")}>)
|
327
|
+
end
|
328
|
+
|
329
|
+
# Internal
|
330
|
+
def custom_members
|
331
|
+
@custom_members ||= {}
|
332
|
+
end
|
333
|
+
|
334
|
+
# Internal
|
335
|
+
if members.first.is_a?(Symbol)
|
336
|
+
def in_member_set?(key)
|
337
|
+
self.class.member_set.include?(key.to_sym)
|
338
|
+
end
|
339
|
+
else
|
340
|
+
def in_member_set?(key)
|
341
|
+
self.class.member_set.include?(key.to_s)
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
# Internal
|
346
|
+
def self.member_set
|
347
|
+
@member_set ||= Set.new(members)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Faraday
|
2
|
+
module NestedParamsEncoder
|
3
|
+
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
4
|
+
|
5
|
+
def self.escape(s)
|
6
|
+
return s.to_s.gsub(ESCAPE_RE) {
|
7
|
+
'%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
|
8
|
+
}.tr(' ', '+')
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.unescape(s)
|
12
|
+
CGI.unescape(s.to_s)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.encode(params)
|
16
|
+
return nil if params == nil
|
17
|
+
|
18
|
+
if !params.is_a?(Array)
|
19
|
+
if !params.respond_to?(:to_hash)
|
20
|
+
raise TypeError,
|
21
|
+
"Can't convert #{params.class} into Hash."
|
22
|
+
end
|
23
|
+
params = params.to_hash
|
24
|
+
params = params.map do |key, value|
|
25
|
+
key = key.to_s if key.kind_of?(Symbol)
|
26
|
+
[key, value]
|
27
|
+
end
|
28
|
+
# Useful default for OAuth and caching.
|
29
|
+
# Only to be used for non-Array inputs. Arrays should preserve order.
|
30
|
+
params.sort!
|
31
|
+
end
|
32
|
+
|
33
|
+
# Helper lambda
|
34
|
+
to_query = lambda do |parent, value|
|
35
|
+
if value.is_a?(Hash)
|
36
|
+
value = value.map do |key, val|
|
37
|
+
key = escape(key)
|
38
|
+
[key, val]
|
39
|
+
end
|
40
|
+
value.sort!
|
41
|
+
buffer = ""
|
42
|
+
value.each do |key, val|
|
43
|
+
new_parent = "#{parent}%5B#{key}%5D"
|
44
|
+
buffer << "#{to_query.call(new_parent, val)}&"
|
45
|
+
end
|
46
|
+
return buffer.chop
|
47
|
+
elsif value.is_a?(Array)
|
48
|
+
buffer = ""
|
49
|
+
value.each_with_index do |val, i|
|
50
|
+
new_parent = "#{parent}%5B%5D"
|
51
|
+
buffer << "#{to_query.call(new_parent, val)}&"
|
52
|
+
end
|
53
|
+
return buffer.chop
|
54
|
+
else
|
55
|
+
encoded_value = escape(value)
|
56
|
+
return "#{parent}=#{encoded_value}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
61
|
+
buffer = ''
|
62
|
+
params.each do |parent, value|
|
63
|
+
encoded_parent = escape(parent)
|
64
|
+
buffer << "#{to_query.call(encoded_parent, value)}&"
|
65
|
+
end
|
66
|
+
return buffer.chop
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.decode(query)
|
70
|
+
return nil if query == nil
|
71
|
+
# Recursive helper lambda
|
72
|
+
dehash = lambda do |hash|
|
73
|
+
hash.each do |(key, value)|
|
74
|
+
if value.kind_of?(Hash)
|
75
|
+
hash[key] = dehash.call(value)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
# Numeric keys implies an array
|
79
|
+
if hash != {} && hash.keys.all? { |key| key =~ /^\d+$/ }
|
80
|
+
hash.sort.inject([]) do |accu, (_, value)|
|
81
|
+
accu << value; accu
|
82
|
+
end
|
83
|
+
else
|
84
|
+
hash
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
empty_accumulator = {}
|
89
|
+
return ((query.split('&').map do |pair|
|
90
|
+
pair.split('=', 2) if pair && !pair.empty?
|
91
|
+
end).compact.inject(empty_accumulator.dup) do |accu, (key, value)|
|
92
|
+
key = unescape(key)
|
93
|
+
if value.kind_of?(String)
|
94
|
+
value = unescape(value.gsub(/\+/, ' '))
|
95
|
+
end
|
96
|
+
|
97
|
+
array_notation = !!(key =~ /\[\]$/)
|
98
|
+
subkeys = key.split(/[\[\]]+/)
|
99
|
+
current_hash = accu
|
100
|
+
for i in 0...(subkeys.size - 1)
|
101
|
+
subkey = subkeys[i]
|
102
|
+
current_hash[subkey] = {} unless current_hash[subkey]
|
103
|
+
current_hash = current_hash[subkey]
|
104
|
+
end
|
105
|
+
if array_notation
|
106
|
+
current_hash[subkeys.last] = [] unless current_hash[subkeys.last]
|
107
|
+
current_hash[subkeys.last] << value
|
108
|
+
else
|
109
|
+
current_hash[subkeys.last] = value
|
110
|
+
end
|
111
|
+
accu
|
112
|
+
end).inject(empty_accumulator.dup) do |accu, (key, value)|
|
113
|
+
accu[key] = value.kind_of?(Hash) ? dehash.call(value) : value
|
114
|
+
accu
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
module FlatParamsEncoder
|
120
|
+
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
121
|
+
|
122
|
+
def self.escape(s)
|
123
|
+
return s.to_s.gsub(ESCAPE_RE) {
|
124
|
+
'%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
|
125
|
+
}.tr(' ', '+')
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.unescape(s)
|
129
|
+
CGI.unescape(s.to_s)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.encode(params)
|
133
|
+
return nil if params == nil
|
134
|
+
|
135
|
+
if !params.is_a?(Array)
|
136
|
+
if !params.respond_to?(:to_hash)
|
137
|
+
raise TypeError,
|
138
|
+
"Can't convert #{params.class} into Hash."
|
139
|
+
end
|
140
|
+
params = params.to_hash
|
141
|
+
params = params.map do |key, value|
|
142
|
+
key = key.to_s if key.kind_of?(Symbol)
|
143
|
+
[key, value]
|
144
|
+
end
|
145
|
+
# Useful default for OAuth and caching.
|
146
|
+
# Only to be used for non-Array inputs. Arrays should preserve order.
|
147
|
+
params.sort!
|
148
|
+
end
|
149
|
+
|
150
|
+
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
151
|
+
buffer = ''
|
152
|
+
params.each do |key, value|
|
153
|
+
encoded_key = escape(key)
|
154
|
+
value = value.to_s if value == true || value == false
|
155
|
+
if value == nil
|
156
|
+
buffer << "#{encoded_key}&"
|
157
|
+
elsif value.kind_of?(Array)
|
158
|
+
value.each do |sub_value|
|
159
|
+
encoded_value = escape(sub_value)
|
160
|
+
buffer << "#{encoded_key}=#{encoded_value}&"
|
161
|
+
end
|
162
|
+
else
|
163
|
+
encoded_value = escape(value)
|
164
|
+
buffer << "#{encoded_key}=#{encoded_value}&"
|
165
|
+
end
|
166
|
+
end
|
167
|
+
return buffer.chop
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.decode(query)
|
171
|
+
empty_accumulator = {}
|
172
|
+
return nil if query == nil
|
173
|
+
split_query = (query.split('&').map do |pair|
|
174
|
+
pair.split('=', 2) if pair && !pair.empty?
|
175
|
+
end).compact
|
176
|
+
return split_query.inject(empty_accumulator.dup) do |accu, pair|
|
177
|
+
pair[0] = unescape(pair[0])
|
178
|
+
pair[1] = true if pair[1].nil?
|
179
|
+
if pair[1].respond_to?(:to_str)
|
180
|
+
pair[1] = unescape(pair[1].to_str.gsub(/\+/, " "))
|
181
|
+
end
|
182
|
+
if accu[pair[0]].kind_of?(Array)
|
183
|
+
accu[pair[0]] << pair[1]
|
184
|
+
elsif accu[pair[0]]
|
185
|
+
accu[pair[0]] = [accu[pair[0]], pair[1]]
|
186
|
+
else
|
187
|
+
accu[pair[0]] = pair[1]
|
188
|
+
end
|
189
|
+
accu
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module Faraday
|
2
|
-
#
|
2
|
+
# A Builder that processes requests into responses by passing through an inner
|
3
|
+
# middleware stack (heavily inspired by Rack).
|
3
4
|
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
class
|
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
|
9
10
|
attr_accessor :handlers
|
10
11
|
|
11
12
|
# Error raised when trying to modify the stack after calling `lock!`
|
@@ -14,15 +15,19 @@ module Faraday
|
|
14
15
|
# borrowed from ActiveSupport::Dependencies::Reference &
|
15
16
|
# ActionDispatch::MiddlewareStack::Middleware
|
16
17
|
class Handler
|
18
|
+
@@constants_mutex = Mutex.new
|
17
19
|
@@constants = Hash.new { |h, k|
|
18
|
-
|
20
|
+
value = k.respond_to?(:constantize) ? k.constantize : Object.const_get(k)
|
21
|
+
@@constants_mutex.synchronize { h[k] = value }
|
19
22
|
}
|
20
23
|
|
21
24
|
attr_reader :name
|
22
25
|
|
23
26
|
def initialize(klass, *args, &block)
|
24
27
|
@name = klass.to_s
|
25
|
-
|
28
|
+
if klass.respond_to?(:name)
|
29
|
+
@@constants_mutex.synchronize { @@constants[@name] = klass }
|
30
|
+
end
|
26
31
|
@args, @block = args, block
|
27
32
|
end
|
28
33
|
|
@@ -58,26 +63,13 @@ module Faraday
|
|
58
63
|
def build(options = {})
|
59
64
|
raise_if_locked
|
60
65
|
@handlers.clear unless options[:keep]
|
61
|
-
yield
|
66
|
+
yield(self) if block_given?
|
62
67
|
end
|
63
68
|
|
64
69
|
def [](idx)
|
65
70
|
@handlers[idx]
|
66
71
|
end
|
67
72
|
|
68
|
-
def ==(other)
|
69
|
-
other.is_a?(self.class) && @handlers == other.handlers
|
70
|
-
end
|
71
|
-
|
72
|
-
def dup
|
73
|
-
self.class.new(@handlers.dup)
|
74
|
-
end
|
75
|
-
|
76
|
-
def to_app(inner_app)
|
77
|
-
# last added handler is the deepest and thus closest to the inner app
|
78
|
-
@handlers.reverse.inject(inner_app) { |app, handler| handler.build(app) }
|
79
|
-
end
|
80
|
-
|
81
73
|
# Locks the middleware stack to ensure no further modifications are possible.
|
82
74
|
def lock!
|
83
75
|
@handlers.freeze
|
@@ -136,6 +128,71 @@ module Faraday
|
|
136
128
|
@handlers.delete(handler)
|
137
129
|
end
|
138
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
|
+
|
139
196
|
private
|
140
197
|
|
141
198
|
def raise_if_locked
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Faraday
|
2
2
|
class Request::Authorization < Faraday::Middleware
|
3
|
-
KEY = "Authorization".freeze
|
3
|
+
KEY = "Authorization".freeze unless defined? KEY
|
4
4
|
|
5
5
|
# Public
|
6
6
|
def self.header(type, token)
|
7
7
|
case token
|
8
|
-
when String, Symbol
|
9
|
-
|
8
|
+
when String, Symbol
|
9
|
+
"#{type} #{token}"
|
10
|
+
when Hash
|
11
|
+
build_hash(type.to_s, token)
|
10
12
|
else
|
11
13
|
raise ArgumentError, "Can't build an Authorization #{type} header from #{token.inspect}"
|
12
14
|
end
|
@@ -30,8 +32,8 @@ module Faraday
|
|
30
32
|
|
31
33
|
# Public
|
32
34
|
def call(env)
|
33
|
-
unless env
|
34
|
-
env
|
35
|
+
unless env.request_headers[KEY]
|
36
|
+
env.request_headers[KEY] = @header_value
|
35
37
|
end
|
36
38
|
@app.call(env)
|
37
39
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'base64'
|
2
2
|
|
3
3
|
module Faraday
|
4
|
-
class Request::BasicAuthentication < Request
|
4
|
+
class Request::BasicAuthentication < Request.load_middleware(:authorization)
|
5
5
|
# Public
|
6
6
|
def self.header(login, pass)
|
7
7
|
value = Base64.encode64([login, pass].join(':'))
|