faraday 0.6.0 → 0.8.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.
- data/Gemfile +22 -15
- data/{LICENSE → LICENSE.md} +1 -1
- data/README.md +216 -146
- data/Rakefile +14 -58
- data/config.ru +6 -0
- data/faraday.gemspec +31 -38
- data/lib/faraday/adapter/em_http.rb +204 -0
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
- data/lib/faraday/adapter/em_synchrony.rb +67 -16
- data/lib/faraday/adapter/excon.rb +1 -2
- data/lib/faraday/adapter/net_http.rb +82 -39
- data/lib/faraday/adapter/net_http_persistent.rb +37 -0
- data/lib/faraday/adapter/patron.rb +42 -8
- data/lib/faraday/adapter/rack.rb +59 -0
- data/lib/faraday/adapter/test.rb +49 -9
- data/lib/faraday/adapter/typhoeus.rb +70 -15
- data/lib/faraday/adapter.rb +41 -18
- data/lib/faraday/builder.rb +34 -16
- data/lib/faraday/connection.rb +177 -115
- data/lib/faraday/error.rb +4 -2
- data/lib/faraday/middleware.rb +18 -10
- data/lib/faraday/request/authorization.rb +40 -0
- data/lib/faraday/request/basic_authentication.rb +13 -0
- data/lib/faraday/request/multipart.rb +5 -6
- data/lib/faraday/request/retry.rb +21 -0
- data/lib/faraday/request/token_authentication.rb +15 -0
- data/lib/faraday/request/url_encoded.rb +1 -3
- data/lib/faraday/request.rb +48 -38
- data/lib/faraday/response/raise_error.rb +1 -1
- data/lib/faraday/response.rb +6 -5
- data/lib/faraday/upload_io.rb +1 -1
- data/lib/faraday/utils.rb +196 -29
- data/lib/faraday.rb +69 -13
- data/test/adapters/default_test.rb +14 -0
- data/test/adapters/em_http_test.rb +14 -0
- data/test/adapters/em_synchrony_test.rb +14 -0
- data/test/adapters/excon_test.rb +23 -0
- data/test/adapters/integration.rb +190 -0
- data/test/adapters/logger_test.rb +2 -2
- data/test/adapters/net_http_persistent_test.rb +11 -0
- data/test/adapters/net_http_test.rb +37 -21
- data/test/adapters/patron_test.rb +17 -0
- data/test/adapters/rack_test.rb +26 -0
- data/test/adapters/test_middleware_test.rb +28 -1
- data/test/adapters/typhoeus_test.rb +14 -0
- data/test/authentication_middleware_test.rb +65 -0
- data/test/connection_test.rb +211 -81
- data/test/env_test.rb +80 -31
- data/test/helper.rb +23 -13
- data/test/live_server.rb +38 -29
- data/test/middleware/retry_test.rb +25 -0
- data/test/middleware_stack_test.rb +85 -4
- data/test/request_middleware_test.rb +42 -19
- data/test/response_middleware_test.rb +30 -3
- metadata +104 -110
- data/lib/faraday/adapter/action_dispatch.rb +0 -30
- data/lib/faraday/request/json.rb +0 -31
- data/test/adapters/live_test.rb +0 -186
data/faraday.gemspec
CHANGED
|
@@ -1,59 +1,41 @@
|
|
|
1
|
-
## This is the rakegem gemspec template. Make sure you read and understand
|
|
2
|
-
## all of the comments. Some sections require modification, and others can
|
|
3
|
-
## be deleted if you don't need them. Once you understand the contents of
|
|
4
|
-
## this file, feel free to delete any comments that begin with two hash marks.
|
|
5
|
-
## You can find comprehensive Gem::Specification documentation, at
|
|
6
|
-
## http://docs.rubygems.org/read/chapter/20
|
|
7
1
|
Gem::Specification.new do |s|
|
|
8
2
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
|
9
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 1.3.5") if s.respond_to? :required_rubygems_version=
|
|
10
4
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
## the sub! line in the Rakefile
|
|
14
|
-
s.name = 'faraday'
|
|
15
|
-
s.version = '0.6.0'
|
|
16
|
-
s.date = '2011-03-31'
|
|
17
|
-
s.rubyforge_project = 'faraday'
|
|
5
|
+
s.name = 'faraday'
|
|
6
|
+
s.version = '0.8.0'
|
|
18
7
|
|
|
19
|
-
## Make sure your summary is short. The description may be as long
|
|
20
|
-
## as you like.
|
|
21
8
|
s.summary = "HTTP/REST API client library."
|
|
22
|
-
s.description
|
|
9
|
+
# TODO: s.description
|
|
23
10
|
|
|
24
|
-
## List the primary authors. If there are a bunch of authors, it's probably
|
|
25
|
-
## better to set the email to an email list or something. If you don't have
|
|
26
|
-
## a custom homepage, consider using your GitHub URL or the like.
|
|
27
11
|
s.authors = ["Rick Olson"]
|
|
28
12
|
s.email = 'technoweenie@gmail.com'
|
|
29
|
-
s.homepage = '
|
|
13
|
+
s.homepage = 'https://github.com/technoweenie/faraday'
|
|
30
14
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
s.
|
|
15
|
+
s.add_dependency 'multipart-post', '~> 1.1'
|
|
16
|
+
s.add_development_dependency 'rake'
|
|
17
|
+
s.add_development_dependency 'simplecov'
|
|
18
|
+
s.add_development_dependency 'test-unit'
|
|
19
|
+
s.add_development_dependency 'webmock'
|
|
34
20
|
|
|
35
|
-
s.add_development_dependency('rake', '~> 0.8')
|
|
36
|
-
s.add_runtime_dependency('addressable', '~> 2.2.4')
|
|
37
|
-
s.add_runtime_dependency('multipart-post', '~> 1.1.0')
|
|
38
|
-
s.add_runtime_dependency('rack', ['>= 1.1.0', "< 2"])
|
|
39
|
-
|
|
40
|
-
## Leave this section as-is. It will be automatically generated from the
|
|
41
|
-
## contents of your Git repository via the gemspec task. DO NOT REMOVE
|
|
42
|
-
## THE MANIFEST COMMENTS, they are used as delimiters by the task.
|
|
43
21
|
# = MANIFEST =
|
|
44
22
|
s.files = %w[
|
|
45
23
|
Gemfile
|
|
46
|
-
LICENSE
|
|
24
|
+
LICENSE.md
|
|
47
25
|
README.md
|
|
48
26
|
Rakefile
|
|
27
|
+
config.ru
|
|
49
28
|
faraday.gemspec
|
|
50
29
|
lib/faraday.rb
|
|
51
30
|
lib/faraday/adapter.rb
|
|
52
|
-
lib/faraday/adapter/
|
|
31
|
+
lib/faraday/adapter/em_http.rb
|
|
53
32
|
lib/faraday/adapter/em_synchrony.rb
|
|
33
|
+
lib/faraday/adapter/em_synchrony/parallel_manager.rb
|
|
54
34
|
lib/faraday/adapter/excon.rb
|
|
55
35
|
lib/faraday/adapter/net_http.rb
|
|
36
|
+
lib/faraday/adapter/net_http_persistent.rb
|
|
56
37
|
lib/faraday/adapter/patron.rb
|
|
38
|
+
lib/faraday/adapter/rack.rb
|
|
57
39
|
lib/faraday/adapter/test.rb
|
|
58
40
|
lib/faraday/adapter/typhoeus.rb
|
|
59
41
|
lib/faraday/builder.rb
|
|
@@ -61,29 +43,40 @@ Gem::Specification.new do |s|
|
|
|
61
43
|
lib/faraday/error.rb
|
|
62
44
|
lib/faraday/middleware.rb
|
|
63
45
|
lib/faraday/request.rb
|
|
64
|
-
lib/faraday/request/
|
|
46
|
+
lib/faraday/request/authorization.rb
|
|
47
|
+
lib/faraday/request/basic_authentication.rb
|
|
65
48
|
lib/faraday/request/multipart.rb
|
|
49
|
+
lib/faraday/request/retry.rb
|
|
50
|
+
lib/faraday/request/token_authentication.rb
|
|
66
51
|
lib/faraday/request/url_encoded.rb
|
|
67
52
|
lib/faraday/response.rb
|
|
68
53
|
lib/faraday/response/logger.rb
|
|
69
54
|
lib/faraday/response/raise_error.rb
|
|
70
55
|
lib/faraday/upload_io.rb
|
|
71
56
|
lib/faraday/utils.rb
|
|
72
|
-
test/adapters/
|
|
57
|
+
test/adapters/default_test.rb
|
|
58
|
+
test/adapters/em_http_test.rb
|
|
59
|
+
test/adapters/em_synchrony_test.rb
|
|
60
|
+
test/adapters/excon_test.rb
|
|
61
|
+
test/adapters/integration.rb
|
|
73
62
|
test/adapters/logger_test.rb
|
|
63
|
+
test/adapters/net_http_persistent_test.rb
|
|
74
64
|
test/adapters/net_http_test.rb
|
|
65
|
+
test/adapters/patron_test.rb
|
|
66
|
+
test/adapters/rack_test.rb
|
|
75
67
|
test/adapters/test_middleware_test.rb
|
|
68
|
+
test/adapters/typhoeus_test.rb
|
|
69
|
+
test/authentication_middleware_test.rb
|
|
76
70
|
test/connection_test.rb
|
|
77
71
|
test/env_test.rb
|
|
78
72
|
test/helper.rb
|
|
79
73
|
test/live_server.rb
|
|
74
|
+
test/middleware/retry_test.rb
|
|
80
75
|
test/middleware_stack_test.rb
|
|
81
76
|
test/request_middleware_test.rb
|
|
82
77
|
test/response_middleware_test.rb
|
|
83
78
|
]
|
|
84
79
|
# = MANIFEST =
|
|
85
80
|
|
|
86
|
-
|
|
87
|
-
## matches what you actually use.
|
|
88
|
-
s.test_files = s.files.select { |path| path =~ /^test\/.*_test\.rb/ }
|
|
81
|
+
s.test_files = s.files.select { |path| path =~ %r{^test/*/.+\.rb} }
|
|
89
82
|
end
|
|
@@ -0,0 +1,204 @@
|
|
|
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
|
+
|
|
8
|
+
dependency 'em-http'
|
|
9
|
+
|
|
10
|
+
self.supports_parallel = true
|
|
11
|
+
|
|
12
|
+
def self.setup_parallel_manager(options = nil)
|
|
13
|
+
Manager.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(env)
|
|
17
|
+
super
|
|
18
|
+
perform_request env
|
|
19
|
+
@app.call env
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def perform_request(env)
|
|
23
|
+
if parallel?(env)
|
|
24
|
+
manager = env[:parallel_manager]
|
|
25
|
+
manager.add {
|
|
26
|
+
perform_single_request(env).
|
|
27
|
+
callback { env[:response].finish(env) }
|
|
28
|
+
}
|
|
29
|
+
else
|
|
30
|
+
unless EventMachine.reactor_running?
|
|
31
|
+
error = nil
|
|
32
|
+
# start EM, block until request is completed
|
|
33
|
+
EventMachine.run do
|
|
34
|
+
perform_single_request(env).
|
|
35
|
+
callback { EventMachine.stop }.
|
|
36
|
+
errback { |client|
|
|
37
|
+
error = error_message(client)
|
|
38
|
+
EventMachine.stop
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
raise_error(error) if error
|
|
42
|
+
else
|
|
43
|
+
# EM is running: instruct upstream that this is an async request
|
|
44
|
+
env[:parallel_manager] = true
|
|
45
|
+
perform_single_request(env).
|
|
46
|
+
callback { env[:response].finish(env) }.
|
|
47
|
+
errback {
|
|
48
|
+
# TODO: no way to communicate the error in async mode
|
|
49
|
+
raise NotImplementedError
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# TODO: reuse the connection to support pipelining
|
|
56
|
+
def perform_single_request(env)
|
|
57
|
+
req = EventMachine::HttpRequest.new(env[:url], connection_config(env))
|
|
58
|
+
req.setup_request(env[:method], request_config(env)).callback { |client|
|
|
59
|
+
save_response(env, client.response_header.status, client.response) do |resp_headers|
|
|
60
|
+
client.response_header.each do |name, value|
|
|
61
|
+
resp_headers[name.to_sym] = value
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def error_message(client)
|
|
68
|
+
client.error or "request failed"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def raise_error(msg)
|
|
72
|
+
errklass = Faraday::Error::ClientError
|
|
73
|
+
if msg == Errno::ETIMEDOUT
|
|
74
|
+
errklass = Faraday::Error::TimeoutError
|
|
75
|
+
msg = "request timed out"
|
|
76
|
+
elsif msg == Errno::ECONNREFUSED
|
|
77
|
+
errklass = Faraday::Error::ConnectionFailed
|
|
78
|
+
msg = "connection refused"
|
|
79
|
+
end
|
|
80
|
+
raise errklass, msg
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def connection_config(env)
|
|
84
|
+
options = {}
|
|
85
|
+
configure_ssl(options, env)
|
|
86
|
+
configure_proxy(options, env)
|
|
87
|
+
configure_timeout(options, env)
|
|
88
|
+
options
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def request_config(env)
|
|
92
|
+
options = {
|
|
93
|
+
:body => read_body(env),
|
|
94
|
+
:head => env[:request_headers],
|
|
95
|
+
# :keepalive => true,
|
|
96
|
+
# :file => 'path/to/file', # stream data off disk
|
|
97
|
+
}
|
|
98
|
+
configure_compression(options, env)
|
|
99
|
+
# configure_proxy_auth
|
|
100
|
+
# :proxy => {:authorization => [user, pass]}
|
|
101
|
+
# proxy[:username] && proxy[:password]
|
|
102
|
+
options
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def read_body(env)
|
|
106
|
+
body = env[:body]
|
|
107
|
+
body.respond_to?(:read) ? body.read : body
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def configure_ssl(options, env)
|
|
111
|
+
if ssl = env[:ssl]
|
|
112
|
+
# :ssl => {
|
|
113
|
+
# :private_key_file => '/tmp/server.key',
|
|
114
|
+
# :cert_chain_file => '/tmp/server.crt',
|
|
115
|
+
# :verify_peer => false
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def configure_proxy(options, env)
|
|
120
|
+
if proxy = request_options(env)[:proxy]
|
|
121
|
+
options[:proxy] = {
|
|
122
|
+
:host => proxy[:uri].host,
|
|
123
|
+
:port => proxy[:uri].port
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def configure_timeout(options, env)
|
|
129
|
+
timeout, open_timeout = request_options(env).values_at(:timeout, :open_timeout)
|
|
130
|
+
options[:connect_timeout] = options[:inactivity_timeout] = timeout
|
|
131
|
+
options[:connect_timeout] = open_timeout if open_timeout
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def configure_compression(options, env)
|
|
135
|
+
if env[:method] == :get and not options[:head].key? 'accept-encoding'
|
|
136
|
+
options[:head]['accept-encoding'] = 'gzip, compressed'
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def request_options(env)
|
|
141
|
+
env[:request]
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def parallel?(env)
|
|
145
|
+
!!env[:parallel_manager]
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# The parallel manager is designed to start an EventMachine loop
|
|
149
|
+
# and block until all registered requests have been completed.
|
|
150
|
+
class Manager
|
|
151
|
+
def initialize
|
|
152
|
+
reset
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def reset
|
|
156
|
+
@registered_procs = []
|
|
157
|
+
@num_registered = 0
|
|
158
|
+
@num_succeeded = 0
|
|
159
|
+
@errors = []
|
|
160
|
+
@running = false
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def running?() @running end
|
|
164
|
+
|
|
165
|
+
def add
|
|
166
|
+
if running?
|
|
167
|
+
perform_request { yield }
|
|
168
|
+
else
|
|
169
|
+
@registered_procs << Proc.new
|
|
170
|
+
end
|
|
171
|
+
@num_registered += 1
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def run
|
|
175
|
+
if @num_registered > 0
|
|
176
|
+
@running = true
|
|
177
|
+
EventMachine.run do
|
|
178
|
+
@registered_procs.each do |proc|
|
|
179
|
+
perform_request(&proc)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
if @errors.size > 0
|
|
183
|
+
raise Faraday::Error::ClientError, @errors.first || "connection failed"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
ensure
|
|
187
|
+
reset
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def perform_request
|
|
191
|
+
client = yield
|
|
192
|
+
client.callback { @num_succeeded += 1; check_finished }
|
|
193
|
+
client.errback { @errors << client.error; check_finished }
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def check_finished
|
|
197
|
+
if @num_succeeded + @errors.size == @num_registered
|
|
198
|
+
EventMachine.stop
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Faraday
|
|
2
|
+
class Adapter
|
|
3
|
+
class EMSynchrony < Faraday::Adapter
|
|
4
|
+
class ParallelManager
|
|
5
|
+
|
|
6
|
+
# Add requests to queue. The `request` argument should be a
|
|
7
|
+
# `EM::HttpRequest` object.
|
|
8
|
+
def add(request, method, *args, &block)
|
|
9
|
+
queue << {
|
|
10
|
+
:request => request,
|
|
11
|
+
:method => method,
|
|
12
|
+
:args => args,
|
|
13
|
+
:block => block
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Run all requests on queue with `EM::Synchrony::Multi`, wrapping
|
|
18
|
+
# it in a reactor and fiber if needed.
|
|
19
|
+
def run
|
|
20
|
+
result = nil
|
|
21
|
+
if !EM.reactor_running?
|
|
22
|
+
EM.run {
|
|
23
|
+
Fiber.new do
|
|
24
|
+
result = perform
|
|
25
|
+
EM.stop
|
|
26
|
+
end.resume
|
|
27
|
+
}
|
|
28
|
+
else
|
|
29
|
+
result = perform
|
|
30
|
+
end
|
|
31
|
+
result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# The request queue.
|
|
38
|
+
def queue
|
|
39
|
+
@queue ||= []
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Main `EM::Synchrony::Multi` performer.
|
|
43
|
+
def perform
|
|
44
|
+
multi = ::EM::Synchrony::Multi.new
|
|
45
|
+
|
|
46
|
+
queue.each do |item|
|
|
47
|
+
method = "a#{item[:method]}".to_sym
|
|
48
|
+
|
|
49
|
+
req = item[:request].send(method, *item[:args])
|
|
50
|
+
req.callback(&item[:block])
|
|
51
|
+
|
|
52
|
+
req_name = "req_#{multi.requests.size}".to_sym
|
|
53
|
+
multi.add(req_name, req)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Clear the queue, so parallel manager objects can be reused.
|
|
57
|
+
@queue = []
|
|
58
|
+
|
|
59
|
+
# Block fiber until all requests have returned.
|
|
60
|
+
multi.perform
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end # ParallelManager
|
|
64
|
+
end # EMSynchrony
|
|
65
|
+
end # Adapter
|
|
66
|
+
end # Faraday
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
+
require 'uri'
|
|
2
|
+
|
|
1
3
|
module Faraday
|
|
2
4
|
class Adapter
|
|
3
5
|
class EMSynchrony < Faraday::Adapter
|
|
6
|
+
|
|
4
7
|
dependency do
|
|
5
8
|
require 'em-synchrony/em-http'
|
|
9
|
+
require 'em-synchrony/em-multi'
|
|
6
10
|
require 'fiber'
|
|
7
11
|
end
|
|
8
12
|
|
|
13
|
+
self.supports_parallel = true
|
|
14
|
+
|
|
15
|
+
def self.setup_parallel_manager(options = {})
|
|
16
|
+
ParallelManager.new
|
|
17
|
+
end
|
|
18
|
+
|
|
9
19
|
def call(env)
|
|
10
20
|
super
|
|
11
21
|
request = EventMachine::HttpRequest.new(URI::parse(env[:url].to_s))
|
|
@@ -22,10 +32,10 @@ module Faraday
|
|
|
22
32
|
|
|
23
33
|
if req = env[:request]
|
|
24
34
|
if proxy = req[:proxy]
|
|
25
|
-
uri =
|
|
35
|
+
uri = URI.parse(proxy[:uri])
|
|
26
36
|
options[:proxy] = {
|
|
27
37
|
:host => uri.host,
|
|
28
|
-
:port => uri.
|
|
38
|
+
:port => uri.port
|
|
29
39
|
}
|
|
30
40
|
if proxy[:username] && proxy[:password]
|
|
31
41
|
options[:proxy][:authorization] = [proxy[:username], proxy[:password]]
|
|
@@ -38,23 +48,43 @@ module Faraday
|
|
|
38
48
|
end
|
|
39
49
|
end
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
51
|
+
http_method = env[:method].to_s.downcase.to_sym
|
|
52
|
+
|
|
53
|
+
# Queue requests for parallel execution.
|
|
54
|
+
if env[:parallel_manager]
|
|
55
|
+
env[:parallel_manager].add(request, http_method, options) do |resp|
|
|
56
|
+
save_response(env, resp.response_header.status, resp.response) do |resp_headers|
|
|
57
|
+
resp.response_header.each do |name, value|
|
|
58
|
+
resp_headers[name.to_sym] = value
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Finalize the response object with values from `env`.
|
|
63
|
+
env[:response].finish(env)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Execute single request.
|
|
50
67
|
else
|
|
51
|
-
client =
|
|
52
|
-
|
|
68
|
+
client = nil
|
|
69
|
+
block = lambda { request.send(http_method, options) }
|
|
70
|
+
|
|
71
|
+
if !EM.reactor_running?
|
|
72
|
+
EM.run do
|
|
73
|
+
Fiber.new {
|
|
74
|
+
client = block.call
|
|
75
|
+
EM.stop
|
|
76
|
+
}.resume
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
client = block.call
|
|
80
|
+
end
|
|
53
81
|
|
|
54
|
-
|
|
55
|
-
|
|
82
|
+
save_response(env, client.response_header.status, client.response) do |resp_headers|
|
|
83
|
+
client.response_header.each do |name, value|
|
|
84
|
+
resp_headers[name.to_sym] = value
|
|
85
|
+
end
|
|
86
|
+
end
|
|
56
87
|
end
|
|
57
|
-
env.update :status => client.response_header.status, :body => client.response
|
|
58
88
|
|
|
59
89
|
@app.call env
|
|
60
90
|
rescue Errno::ECONNREFUSED
|
|
@@ -63,3 +93,24 @@ module Faraday
|
|
|
63
93
|
end
|
|
64
94
|
end
|
|
65
95
|
end
|
|
96
|
+
|
|
97
|
+
require 'faraday/adapter/em_synchrony/parallel_manager'
|
|
98
|
+
|
|
99
|
+
# add missing patch(), options() methods
|
|
100
|
+
EventMachine::HTTPMethods.module_eval do
|
|
101
|
+
([:patch, :options] - instance_methods).each do |type|
|
|
102
|
+
module_eval %[
|
|
103
|
+
def #{type}(options = {}, &blk)
|
|
104
|
+
f = Fiber.current
|
|
105
|
+
conn = setup_request(:#{type}, options, &blk)
|
|
106
|
+
if conn.error.nil?
|
|
107
|
+
conn.callback { f.resume(conn) }
|
|
108
|
+
conn.errback { f.resume(conn) }
|
|
109
|
+
Fiber.yield
|
|
110
|
+
else
|
|
111
|
+
conn
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
]
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -17,8 +17,7 @@ module Faraday
|
|
|
17
17
|
:headers => env[:request_headers],
|
|
18
18
|
:body => env[:body]
|
|
19
19
|
|
|
20
|
-
env
|
|
21
|
-
response_headers(env).update resp.headers
|
|
20
|
+
save_response(env, resp.status.to_i, resp.body, resp.headers)
|
|
22
21
|
|
|
23
22
|
@app.call env
|
|
24
23
|
rescue ::Excon::Errors::SocketError
|
|
@@ -8,64 +8,107 @@ end
|
|
|
8
8
|
module Faraday
|
|
9
9
|
class Adapter
|
|
10
10
|
class NetHttp < Faraday::Adapter
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
NET_HTTP_EXCEPTIONS = [
|
|
12
|
+
EOFError,
|
|
13
|
+
Errno::ECONNABORTED,
|
|
14
|
+
Errno::ECONNREFUSED,
|
|
15
|
+
Errno::ECONNRESET,
|
|
16
|
+
Errno::EINVAL,
|
|
17
|
+
Net::HTTPBadResponse,
|
|
18
|
+
Net::HTTPHeaderSyntaxError,
|
|
19
|
+
Net::ProtocolError,
|
|
20
|
+
SocketError
|
|
21
|
+
]
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
http.cert = ssl[:client_cert] if ssl[:client_cert]
|
|
23
|
-
http.key = ssl[:client_key] if ssl[:client_key]
|
|
24
|
-
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
|
|
25
|
-
http.cert_store = ssl[:cert_store] if ssl[:cert_store]
|
|
26
|
-
end
|
|
25
|
+
def call(env)
|
|
26
|
+
super
|
|
27
|
+
http = net_http_connection(env)
|
|
28
|
+
configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
|
|
27
29
|
|
|
30
|
+
req = env[:request]
|
|
28
31
|
http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
|
|
29
32
|
http.open_timeout = req[:open_timeout] if req[:open_timeout]
|
|
30
33
|
|
|
31
|
-
if :get != env[:method]
|
|
32
|
-
http_request = Net::HTTPGenericRequest.new \
|
|
33
|
-
env[:method].to_s.upcase, # request method
|
|
34
|
-
!!env[:body], # is there data
|
|
35
|
-
true, # does net/http love you, true or false?
|
|
36
|
-
url.request_uri, # request uri path
|
|
37
|
-
env[:request_headers] # request headers
|
|
38
|
-
|
|
39
|
-
if env[:body].respond_to?(:read)
|
|
40
|
-
http_request.body_stream = env[:body]
|
|
41
|
-
env[:body] = nil
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
34
|
begin
|
|
46
|
-
http_response =
|
|
47
|
-
|
|
48
|
-
http.get url.request_uri, env[:request_headers]
|
|
49
|
-
else
|
|
50
|
-
http.request http_request, env[:body]
|
|
51
|
-
end
|
|
52
|
-
rescue Errno::ECONNREFUSED
|
|
35
|
+
http_response = perform_request(http, env)
|
|
36
|
+
rescue *NET_HTTP_EXCEPTIONS
|
|
53
37
|
raise Error::ConnectionFailed, $!
|
|
54
38
|
end
|
|
55
39
|
|
|
56
|
-
http_response.
|
|
57
|
-
|
|
40
|
+
save_response(env, http_response.code.to_i, http_response.body) do |response_headers|
|
|
41
|
+
http_response.each_header do |key, value|
|
|
42
|
+
response_headers[key] = value
|
|
43
|
+
end
|
|
58
44
|
end
|
|
59
|
-
env.update :status => http_response.code.to_i, :body => http_response.body
|
|
60
45
|
|
|
61
46
|
@app.call env
|
|
47
|
+
rescue Timeout::Error => err
|
|
48
|
+
raise Faraday::Error::TimeoutError, err
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def create_request(env)
|
|
52
|
+
request = Net::HTTPGenericRequest.new \
|
|
53
|
+
env[:method].to_s.upcase, # request method
|
|
54
|
+
!!env[:body], # is there request body
|
|
55
|
+
:head != env[:method], # is there response body
|
|
56
|
+
env[:url].request_uri, # request uri path
|
|
57
|
+
env[:request_headers] # request headers
|
|
58
|
+
|
|
59
|
+
if env[:body].respond_to?(:read)
|
|
60
|
+
request.body_stream = env[:body]
|
|
61
|
+
else
|
|
62
|
+
request.body = env[:body]
|
|
63
|
+
end
|
|
64
|
+
request
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def perform_request(http, env)
|
|
68
|
+
if :get == env[:method] and !env[:body]
|
|
69
|
+
# prefer `get` to `request` because the former handles gzip (ruby 1.9)
|
|
70
|
+
http.get env[:url].request_uri, env[:request_headers]
|
|
71
|
+
else
|
|
72
|
+
http.request create_request(env)
|
|
73
|
+
end
|
|
62
74
|
end
|
|
63
75
|
|
|
64
|
-
def
|
|
76
|
+
def net_http_connection(env)
|
|
65
77
|
if proxy = env[:request][:proxy]
|
|
66
78
|
Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
|
|
67
79
|
else
|
|
68
80
|
Net::HTTP
|
|
81
|
+
end.new(env[:url].host, env[:url].port)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def configure_ssl(http, ssl)
|
|
85
|
+
http.use_ssl = true
|
|
86
|
+
http.verify_mode = ssl_verify_mode(ssl)
|
|
87
|
+
http.cert_store = ssl_cert_store(ssl)
|
|
88
|
+
|
|
89
|
+
http.cert = ssl[:client_cert] if ssl[:client_cert]
|
|
90
|
+
http.key = ssl[:client_key] if ssl[:client_key]
|
|
91
|
+
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
|
|
92
|
+
http.ca_path = ssl[:ca_path] if ssl[:ca_path]
|
|
93
|
+
http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
|
|
94
|
+
http.ssl_version = ssl[:version] if ssl[:version]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def ssl_cert_store(ssl)
|
|
98
|
+
return ssl[:cert_store] if ssl[:cert_store]
|
|
99
|
+
# Use the default cert store by default, i.e. system ca certs
|
|
100
|
+
cert_store = OpenSSL::X509::Store.new
|
|
101
|
+
cert_store.set_default_paths
|
|
102
|
+
cert_store
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def ssl_verify_mode(ssl)
|
|
106
|
+
ssl[:verify_mode] || begin
|
|
107
|
+
if ssl.fetch(:verify, true)
|
|
108
|
+
OpenSSL::SSL::VERIFY_PEER
|
|
109
|
+
else
|
|
110
|
+
OpenSSL::SSL::VERIFY_NONE
|
|
111
|
+
end
|
|
69
112
|
end
|
|
70
113
|
end
|
|
71
114
|
end
|