avdi-faraday 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|