avdi-faraday 0.8.1
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 +27 -0
- data/LICENSE.md +20 -0
- data/README.md +250 -0
- data/Rakefile +87 -0
- data/config.ru +6 -0
- data/faraday.gemspec +86 -0
- data/lib/faraday.rb +276 -0
- data/lib/faraday/adapter.rb +71 -0
- data/lib/faraday/adapter/em_http.rb +217 -0
- data/lib/faraday/adapter/em_synchrony.rb +89 -0
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
- data/lib/faraday/adapter/excon.rb +59 -0
- data/lib/faraday/adapter/httpclient.rb +92 -0
- data/lib/faraday/adapter/net_http.rb +116 -0
- data/lib/faraday/adapter/net_http_persistent.rb +37 -0
- data/lib/faraday/adapter/patron.rb +65 -0
- data/lib/faraday/adapter/rack.rb +57 -0
- data/lib/faraday/adapter/test.rb +162 -0
- data/lib/faraday/adapter/typhoeus.rb +107 -0
- data/lib/faraday/builder.rb +184 -0
- data/lib/faraday/connection.rb +468 -0
- data/lib/faraday/error.rb +40 -0
- data/lib/faraday/middleware.rb +41 -0
- data/lib/faraday/request.rb +101 -0
- data/lib/faraday/request/authorization.rb +40 -0
- data/lib/faraday/request/basic_authentication.rb +13 -0
- data/lib/faraday/request/multipart.rb +62 -0
- data/lib/faraday/request/retry.rb +67 -0
- data/lib/faraday/request/token_authentication.rb +15 -0
- data/lib/faraday/request/url_encoded.rb +35 -0
- data/lib/faraday/response.rb +99 -0
- data/lib/faraday/response/logger.rb +34 -0
- data/lib/faraday/response/raise_error.rb +16 -0
- data/lib/faraday/upload_io.rb +23 -0
- data/lib/faraday/utils.rb +274 -0
- data/script/test +91 -0
- data/test/adapters/default_test.rb +14 -0
- data/test/adapters/em_http_test.rb +19 -0
- data/test/adapters/em_synchrony_test.rb +20 -0
- data/test/adapters/excon_test.rb +15 -0
- data/test/adapters/httpclient_test.rb +16 -0
- data/test/adapters/integration.rb +193 -0
- data/test/adapters/logger_test.rb +37 -0
- data/test/adapters/net_http_persistent_test.rb +11 -0
- data/test/adapters/net_http_test.rb +49 -0
- data/test/adapters/patron_test.rb +17 -0
- data/test/adapters/rack_test.rb +26 -0
- data/test/adapters/test_middleware_test.rb +70 -0
- data/test/adapters/typhoeus_test.rb +20 -0
- data/test/authentication_middleware_test.rb +65 -0
- data/test/connection_test.rb +375 -0
- data/test/env_test.rb +183 -0
- data/test/helper.rb +75 -0
- data/test/live_server.rb +57 -0
- data/test/middleware/retry_test.rb +62 -0
- data/test/middleware_stack_test.rb +203 -0
- data/test/middleware_test.rb +12 -0
- data/test/request_middleware_test.rb +108 -0
- data/test/response_middleware_test.rb +74 -0
- metadata +182 -0
data/lib/faraday.rb
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
# Public: This is the main namespace for Faraday. You can either use it to
|
2
|
+
# create Faraday::Connection objects, or access it directly.
|
3
|
+
#
|
4
|
+
# Examples
|
5
|
+
#
|
6
|
+
# Faraday.get "http://faraday.com"
|
7
|
+
#
|
8
|
+
# conn = Faraday.new "http://faraday.com"
|
9
|
+
# conn.get '/'
|
10
|
+
#
|
11
|
+
module Faraday
|
12
|
+
VERSION = "0.8.1"
|
13
|
+
|
14
|
+
class << self
|
15
|
+
# Public: Gets or sets the root path that Faraday is being loaded from.
|
16
|
+
# This is the root from where the libraries are auto-loaded from.
|
17
|
+
attr_accessor :root_path
|
18
|
+
|
19
|
+
# Public: Gets or sets the path that the Faraday libs are loaded from.
|
20
|
+
attr_accessor :lib_path
|
21
|
+
|
22
|
+
# Public: Gets or sets the Symbol key identifying a default Adapter to use
|
23
|
+
# for the default Faraday::Connection.
|
24
|
+
attr_accessor :default_adapter
|
25
|
+
|
26
|
+
# Public: Sets the default Faraday::Connection for simple scripts that
|
27
|
+
# access the Faraday constant directly.
|
28
|
+
#
|
29
|
+
# Faraday.get "https://faraday.com"
|
30
|
+
attr_writer :default_connection
|
31
|
+
|
32
|
+
# Public: Sets the default options used when calling Faraday#new.
|
33
|
+
attr_writer :default_connection_options
|
34
|
+
|
35
|
+
# Public: Initializes a new Faraday::Connection.
|
36
|
+
#
|
37
|
+
# url - The optional String base URL to use as a prefix for all
|
38
|
+
# requests. Can also be the options Hash.
|
39
|
+
# options - The optional Hash used to configure this Faraday::Connection.
|
40
|
+
# Any of these values will be set on every request made, unless
|
41
|
+
# overridden for a specific request.
|
42
|
+
# :url - String base URL.
|
43
|
+
# :params - Hash of URI query unencoded key/value pairs.
|
44
|
+
# :headers - Hash of unencoded HTTP header key/value pairs.
|
45
|
+
# :request - Hash of request options.
|
46
|
+
# :ssl - Hash of SSL options.
|
47
|
+
# :proxy - Hash of Proxy options.
|
48
|
+
#
|
49
|
+
# Examples
|
50
|
+
#
|
51
|
+
# Faraday.new 'http://faraday.com'
|
52
|
+
#
|
53
|
+
# # http://faraday.com?page=1
|
54
|
+
# Faraday.new 'http://faraday.com', :params => {:page => 1}
|
55
|
+
#
|
56
|
+
# # same
|
57
|
+
#
|
58
|
+
# Faraday.new :url => 'http://faraday.com',
|
59
|
+
# :params => {:page => 1}
|
60
|
+
#
|
61
|
+
# Returns a Faraday::Connection.
|
62
|
+
def new(url = nil, options = {})
|
63
|
+
block = block_given? ? Proc.new : nil
|
64
|
+
options = Faraday::Utils.deep_merge(default_connection_options, options)
|
65
|
+
Faraday::Connection.new(url, options, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Internal: Requires internal Faraday libraries.
|
69
|
+
#
|
70
|
+
# *libs - One or more relative String names to Faraday classes.
|
71
|
+
#
|
72
|
+
# Returns nothing.
|
73
|
+
def require_libs(*libs)
|
74
|
+
libs.each do |lib|
|
75
|
+
require "#{lib_path}/#{lib}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
alias require_lib require_libs
|
80
|
+
|
81
|
+
private
|
82
|
+
# Internal: Proxies method calls on the Faraday constant to
|
83
|
+
# #default_connection.
|
84
|
+
def method_missing(name, *args, &block)
|
85
|
+
default_connection.send(name, *args, &block)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
self.root_path = File.expand_path "..", __FILE__
|
90
|
+
self.lib_path = File.expand_path "../faraday", __FILE__
|
91
|
+
self.default_adapter = :net_http
|
92
|
+
|
93
|
+
# Gets the default connection used for simple scripts.
|
94
|
+
#
|
95
|
+
# Returns a Faraday::Connection, configured with the #default_adapter.
|
96
|
+
def self.default_connection
|
97
|
+
@default_connection ||= Connection.new
|
98
|
+
end
|
99
|
+
|
100
|
+
# Gets the default connection options used when calling Faraday#new.
|
101
|
+
#
|
102
|
+
# Returns an options Hash.
|
103
|
+
def self.default_connection_options
|
104
|
+
@default_connection_options ||= {}
|
105
|
+
end
|
106
|
+
|
107
|
+
if (!defined?(RUBY_ENGINE) || "ruby" == RUBY_ENGINE) && RUBY_VERSION < '1.9'
|
108
|
+
begin
|
109
|
+
require 'system_timer'
|
110
|
+
Timer = SystemTimer
|
111
|
+
rescue LoadError
|
112
|
+
warn "Faraday: you may want to install system_timer for reliable timeouts"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
unless const_defined? :Timer
|
117
|
+
require 'timeout'
|
118
|
+
Timer = Timeout
|
119
|
+
end
|
120
|
+
|
121
|
+
# Public: Adds the ability for other modules to register and lookup
|
122
|
+
# middleware classes.
|
123
|
+
module MiddlewareRegistry
|
124
|
+
# Public: Register middleware class(es) on the current module.
|
125
|
+
#
|
126
|
+
# mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
|
127
|
+
# as fully qualified constant, or a Proc that will be lazily
|
128
|
+
# called to return the former.
|
129
|
+
#
|
130
|
+
# Examples
|
131
|
+
#
|
132
|
+
# module Faraday
|
133
|
+
# class Whatever
|
134
|
+
# # Middleware looked up by :foo returns Faraday::Whatever::Foo.
|
135
|
+
# register_middleware :foo => Foo
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
#
|
139
|
+
# Returns nothing.
|
140
|
+
def register_middleware(mapping)
|
141
|
+
(@registered_middleware ||= {}).update(mapping)
|
142
|
+
end
|
143
|
+
|
144
|
+
# Public: Lookup middleware class with a registered Symbol shortcut.
|
145
|
+
#
|
146
|
+
# key - The Symbol key for the registered middleware.
|
147
|
+
#
|
148
|
+
# Examples
|
149
|
+
#
|
150
|
+
# module Faraday
|
151
|
+
# class Whatever
|
152
|
+
# register_middleware :foo => Foo
|
153
|
+
# end
|
154
|
+
# end
|
155
|
+
#
|
156
|
+
# Faraday::Whatever.lookup_middleware(:foo)
|
157
|
+
# # => Faraday::Whatever::Foo
|
158
|
+
#
|
159
|
+
# Returns a middleware Class.
|
160
|
+
def lookup_middleware(key)
|
161
|
+
unless defined? @registered_middleware and found = @registered_middleware[key]
|
162
|
+
raise "#{key.inspect} is not registered on #{self}"
|
163
|
+
end
|
164
|
+
found = @registered_middleware[key] = found.call if found.is_a? Proc
|
165
|
+
found.is_a?(Module) ? found : const_get(found)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Internal: Adds the ability for other modules to manage autoloadable
|
170
|
+
# constants.
|
171
|
+
module AutoloadHelper
|
172
|
+
# Internal: Registers the constants to be auto loaded.
|
173
|
+
#
|
174
|
+
# prefix - The String require prefix. If the path is inside Faraday, then
|
175
|
+
# it will be prefixed with the root path of this loaded Faraday
|
176
|
+
# version.
|
177
|
+
# options - Hash of Symbol => String library names.
|
178
|
+
#
|
179
|
+
# Examples.
|
180
|
+
#
|
181
|
+
# Faraday.autoload_all 'faraday/foo',
|
182
|
+
# :Bar => 'bar'
|
183
|
+
#
|
184
|
+
# # requires faraday/foo/bar to load Faraday::Bar.
|
185
|
+
# Faraday::Bar
|
186
|
+
#
|
187
|
+
#
|
188
|
+
# Returns nothing.
|
189
|
+
def autoload_all(prefix, options)
|
190
|
+
if prefix =~ /^faraday(\/|$)/i
|
191
|
+
prefix = File.join(Faraday.root_path, prefix)
|
192
|
+
end
|
193
|
+
options.each do |const_name, path|
|
194
|
+
autoload const_name, File.join(prefix, path)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
# Internal: Loads each autoloaded constant. If thread safety is a concern,
|
199
|
+
# wrap this in a Mutex.
|
200
|
+
#
|
201
|
+
# Returns nothing.
|
202
|
+
def load_autoloaded_constants
|
203
|
+
constants.each do |const|
|
204
|
+
const_get(const) if autoload?(const)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Internal: Filters the module's contents with those that have been already
|
209
|
+
# autoloaded.
|
210
|
+
#
|
211
|
+
# Returns an Array of Class/Module objects.
|
212
|
+
def all_loaded_constants
|
213
|
+
constants.map { |c| const_get(c) }.
|
214
|
+
select { |a| a.respond_to?(:loaded?) && a.loaded? }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
extend AutoloadHelper
|
219
|
+
|
220
|
+
# Public: Register middleware classes under a short name.
|
221
|
+
#
|
222
|
+
# type - A Symbol specifying the kind of middleware (default: :middleware)
|
223
|
+
# mapping - A Hash mapping Symbol keys to classes. Classes can be expressed
|
224
|
+
# as fully qualified constant, or a Proc that will be lazily called
|
225
|
+
# to return the former.
|
226
|
+
#
|
227
|
+
# Examples
|
228
|
+
#
|
229
|
+
# Faraday.register_middleware :aloha => MyModule::Aloha
|
230
|
+
# Faraday.register_middleware :response, :boom => MyModule::Boom
|
231
|
+
#
|
232
|
+
# # shortcuts are now available in Builder:
|
233
|
+
# builder.use :aloha
|
234
|
+
# builder.response :boom
|
235
|
+
#
|
236
|
+
# Returns nothing.
|
237
|
+
def self.register_middleware(type, mapping = nil)
|
238
|
+
type, mapping = :middleware, type if mapping.nil?
|
239
|
+
component = self.const_get(type.to_s.capitalize)
|
240
|
+
component.register_middleware(mapping)
|
241
|
+
end
|
242
|
+
|
243
|
+
autoload_all "faraday",
|
244
|
+
:Middleware => 'middleware',
|
245
|
+
:Builder => 'builder',
|
246
|
+
:Request => 'request',
|
247
|
+
:Response => 'response',
|
248
|
+
:CompositeReadIO => 'upload_io',
|
249
|
+
:UploadIO => 'upload_io',
|
250
|
+
:Parts => 'upload_io'
|
251
|
+
|
252
|
+
require_libs "utils", "connection", "adapter", "error"
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
# not pulling in active-support JUST for this method. And I love this method.
|
257
|
+
class Object
|
258
|
+
# The primary purpose of this method is to "tap into" a method chain,
|
259
|
+
# in order to perform operations on intermediate results within the chain.
|
260
|
+
#
|
261
|
+
# Examples
|
262
|
+
#
|
263
|
+
# (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
|
264
|
+
# tap { |x| puts "array: #{x.inspect}" }.
|
265
|
+
# select { |x| x%2 == 0 }.
|
266
|
+
# tap { |x| puts "evens: #{x.inspect}" }.
|
267
|
+
# map { |x| x*x }.
|
268
|
+
# tap { |x| puts "squares: #{x.inspect}" }
|
269
|
+
#
|
270
|
+
# Yields self.
|
271
|
+
# Returns self.
|
272
|
+
def tap
|
273
|
+
yield self
|
274
|
+
self
|
275
|
+
end unless Object.respond_to?(:tap)
|
276
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Faraday
|
2
|
+
# Public: This is a base class for all Faraday adapters. Adapters are
|
3
|
+
# responsible for fulfilling a Faraday request.
|
4
|
+
class Adapter < Middleware
|
5
|
+
CONTENT_LENGTH = 'Content-Length'.freeze
|
6
|
+
|
7
|
+
extend AutoloadHelper
|
8
|
+
extend MiddlewareRegistry
|
9
|
+
|
10
|
+
autoload_all 'faraday/adapter',
|
11
|
+
:NetHttp => 'net_http',
|
12
|
+
:NetHttpPersistent => 'net_http_persistent',
|
13
|
+
:Typhoeus => 'typhoeus',
|
14
|
+
:EMSynchrony => 'em_synchrony',
|
15
|
+
:EMHttp => 'em_http',
|
16
|
+
:Patron => 'patron',
|
17
|
+
:Excon => 'excon',
|
18
|
+
:Test => 'test',
|
19
|
+
:Rack => 'rack',
|
20
|
+
:HTTPClient => 'httpclient'
|
21
|
+
|
22
|
+
register_middleware \
|
23
|
+
:test => :Test,
|
24
|
+
:net_http => :NetHttp,
|
25
|
+
:net_http_persistent => :NetHttpPersistent,
|
26
|
+
:typhoeus => :Typhoeus,
|
27
|
+
:patron => :Patron,
|
28
|
+
:em_synchrony => :EMSynchrony,
|
29
|
+
:em_http => :EMHttp,
|
30
|
+
:excon => :Excon,
|
31
|
+
:rack => :Rack,
|
32
|
+
:httpclient => :HTTPClient
|
33
|
+
|
34
|
+
# Public: This module marks an Adapter as supporting parallel requests.
|
35
|
+
module Parallelism
|
36
|
+
attr_writer :supports_parallel
|
37
|
+
def supports_parallel?() @supports_parallel end
|
38
|
+
|
39
|
+
def inherited(subclass)
|
40
|
+
super
|
41
|
+
subclass.supports_parallel = self.supports_parallel?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
extend Parallelism
|
46
|
+
self.supports_parallel = false
|
47
|
+
|
48
|
+
def self.adapter?
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def call(env)
|
53
|
+
if !env[:body] and Connection::METHODS_WITH_BODIES.include? env[:method]
|
54
|
+
# play nice and indicate we're sending an empty body
|
55
|
+
env[:request_headers][CONTENT_LENGTH] = "0"
|
56
|
+
# Typhoeus hangs on PUT requests if body is nil
|
57
|
+
env[:body] = ''
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def save_response(env, status, body, headers = nil)
|
62
|
+
env[:status] = status
|
63
|
+
env[:body] = body
|
64
|
+
env[:response_headers] = Utils::Headers.new.tap do |response_headers|
|
65
|
+
response_headers.update headers unless headers.nil?
|
66
|
+
yield response_headers if block_given?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Adapter
|
3
|
+
# EventMachine adapter is useful for either asynchronous requests
|
4
|
+
# when in EM reactor loop or for making parallel requests in
|
5
|
+
# synchronous code.
|
6
|
+
class EMHttp < Faraday::Adapter
|
7
|
+
module Options
|
8
|
+
def connection_config(env)
|
9
|
+
options = {}
|
10
|
+
configure_ssl(options, env)
|
11
|
+
configure_proxy(options, env)
|
12
|
+
configure_timeout(options, env)
|
13
|
+
configure_socket(options, env)
|
14
|
+
options
|
15
|
+
end
|
16
|
+
|
17
|
+
def request_config(env)
|
18
|
+
options = {
|
19
|
+
:body => read_body(env),
|
20
|
+
:head => env[:request_headers],
|
21
|
+
# :keepalive => true,
|
22
|
+
# :file => 'path/to/file', # stream data off disk
|
23
|
+
}
|
24
|
+
configure_compression(options, env)
|
25
|
+
# configure_proxy_auth
|
26
|
+
# :proxy => {:authorization => [user, pass]}
|
27
|
+
# proxy[:username] && proxy[:password]
|
28
|
+
options
|
29
|
+
end
|
30
|
+
|
31
|
+
def read_body(env)
|
32
|
+
body = env[:body]
|
33
|
+
body.respond_to?(:read) ? body.read : body
|
34
|
+
end
|
35
|
+
|
36
|
+
def configure_ssl(options, env)
|
37
|
+
if ssl = env[:ssl]
|
38
|
+
# :ssl => {
|
39
|
+
# :private_key_file => '/tmp/server.key',
|
40
|
+
# :cert_chain_file => '/tmp/server.crt',
|
41
|
+
# :verify_peer => false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def configure_proxy(options, env)
|
46
|
+
if proxy = request_options(env)[:proxy]
|
47
|
+
options[:proxy] = {
|
48
|
+
:host => proxy[:uri].host,
|
49
|
+
:port => proxy[:uri].port
|
50
|
+
}
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def configure_socket(options, env)
|
55
|
+
if bind = request_options(env)[:bind]
|
56
|
+
options[:bind] = {
|
57
|
+
:host => bind[:host],
|
58
|
+
:port => bind[:port]
|
59
|
+
}
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def configure_timeout(options, env)
|
64
|
+
timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
|
65
|
+
options[:connect_timeout] = options[:inactivity_timeout] = timeout
|
66
|
+
options[:connect_timeout] = open_timeout if open_timeout
|
67
|
+
end
|
68
|
+
|
69
|
+
def configure_compression(options, env)
|
70
|
+
if env[:method] == :get and not options[:head].key? 'accept-encoding'
|
71
|
+
options[:head]['accept-encoding'] = 'gzip, compressed'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def request_options(env)
|
76
|
+
env[:request]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
include Options
|
81
|
+
|
82
|
+
dependency 'em-http'
|
83
|
+
|
84
|
+
self.supports_parallel = true
|
85
|
+
|
86
|
+
def self.setup_parallel_manager(options = nil)
|
87
|
+
Manager.new
|
88
|
+
end
|
89
|
+
|
90
|
+
def call(env)
|
91
|
+
super
|
92
|
+
perform_request env
|
93
|
+
@app.call env
|
94
|
+
end
|
95
|
+
|
96
|
+
def perform_request(env)
|
97
|
+
if parallel?(env)
|
98
|
+
manager = env[:parallel_manager]
|
99
|
+
manager.add {
|
100
|
+
perform_single_request(env).
|
101
|
+
callback { env[:response].finish(env) }
|
102
|
+
}
|
103
|
+
else
|
104
|
+
unless EventMachine.reactor_running?
|
105
|
+
error = nil
|
106
|
+
# start EM, block until request is completed
|
107
|
+
EventMachine.run do
|
108
|
+
perform_single_request(env).
|
109
|
+
callback { EventMachine.stop }.
|
110
|
+
errback { |client|
|
111
|
+
error = error_message(client)
|
112
|
+
EventMachine.stop
|
113
|
+
}
|
114
|
+
end
|
115
|
+
raise_error(error) if error
|
116
|
+
else
|
117
|
+
# EM is running: instruct upstream that this is an async request
|
118
|
+
env[:parallel_manager] = true
|
119
|
+
perform_single_request(env).
|
120
|
+
callback { env[:response].finish(env) }.
|
121
|
+
errback {
|
122
|
+
# TODO: no way to communicate the error in async mode
|
123
|
+
raise NotImplementedError
|
124
|
+
}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# TODO: reuse the connection to support pipelining
|
130
|
+
def perform_single_request(env)
|
131
|
+
req = EventMachine::HttpRequest.new(env[:url], connection_config(env))
|
132
|
+
req.setup_request(env[:method], request_config(env)).callback { |client|
|
133
|
+
save_response(env, client.response_header.status, client.response) do |resp_headers|
|
134
|
+
client.response_header.each do |name, value|
|
135
|
+
resp_headers[name.to_sym] = value
|
136
|
+
end
|
137
|
+
end
|
138
|
+
}
|
139
|
+
end
|
140
|
+
|
141
|
+
def error_message(client)
|
142
|
+
client.error or "request failed"
|
143
|
+
end
|
144
|
+
|
145
|
+
def raise_error(msg)
|
146
|
+
errklass = Faraday::Error::ClientError
|
147
|
+
if msg == Errno::ETIMEDOUT
|
148
|
+
errklass = Faraday::Error::TimeoutError
|
149
|
+
msg = "request timed out"
|
150
|
+
elsif msg == Errno::ECONNREFUSED
|
151
|
+
errklass = Faraday::Error::ConnectionFailed
|
152
|
+
msg = "connection refused"
|
153
|
+
end
|
154
|
+
raise errklass, msg
|
155
|
+
end
|
156
|
+
|
157
|
+
def parallel?(env)
|
158
|
+
!!env[:parallel_manager]
|
159
|
+
end
|
160
|
+
|
161
|
+
# The parallel manager is designed to start an EventMachine loop
|
162
|
+
# and block until all registered requests have been completed.
|
163
|
+
class Manager
|
164
|
+
def initialize
|
165
|
+
reset
|
166
|
+
end
|
167
|
+
|
168
|
+
def reset
|
169
|
+
@registered_procs = []
|
170
|
+
@num_registered = 0
|
171
|
+
@num_succeeded = 0
|
172
|
+
@errors = []
|
173
|
+
@running = false
|
174
|
+
end
|
175
|
+
|
176
|
+
def running?() @running end
|
177
|
+
|
178
|
+
def add
|
179
|
+
if running?
|
180
|
+
perform_request { yield }
|
181
|
+
else
|
182
|
+
@registered_procs << Proc.new
|
183
|
+
end
|
184
|
+
@num_registered += 1
|
185
|
+
end
|
186
|
+
|
187
|
+
def run
|
188
|
+
if @num_registered > 0
|
189
|
+
@running = true
|
190
|
+
EventMachine.run do
|
191
|
+
@registered_procs.each do |proc|
|
192
|
+
perform_request(&proc)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
if @errors.size > 0
|
196
|
+
raise Faraday::Error::ClientError, @errors.first || "connection failed"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
ensure
|
200
|
+
reset
|
201
|
+
end
|
202
|
+
|
203
|
+
def perform_request
|
204
|
+
client = yield
|
205
|
+
client.callback { @num_succeeded += 1; check_finished }
|
206
|
+
client.errback { @errors << client.error; check_finished }
|
207
|
+
end
|
208
|
+
|
209
|
+
def check_finished
|
210
|
+
if @num_succeeded + @errors.size == @num_registered
|
211
|
+
EventMachine.stop
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|