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
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
class Adapter
|
5
|
+
class EMSynchrony < Faraday::Adapter
|
6
|
+
include EMHttp::Options
|
7
|
+
|
8
|
+
dependency do
|
9
|
+
require 'em-synchrony/em-http'
|
10
|
+
require 'em-synchrony/em-multi'
|
11
|
+
require 'fiber'
|
12
|
+
end
|
13
|
+
|
14
|
+
self.supports_parallel = true
|
15
|
+
|
16
|
+
def self.setup_parallel_manager(options = {})
|
17
|
+
ParallelManager.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
super
|
22
|
+
request = EventMachine::HttpRequest.new(URI::parse(env[:url].to_s), connection_config(env))
|
23
|
+
|
24
|
+
http_method = env[:method].to_s.downcase.to_sym
|
25
|
+
|
26
|
+
# Queue requests for parallel execution.
|
27
|
+
if env[:parallel_manager]
|
28
|
+
env[:parallel_manager].add(request, http_method, request_config(env)) do |resp|
|
29
|
+
save_response(env, resp.response_header.status, resp.response) do |resp_headers|
|
30
|
+
resp.response_header.each do |name, value|
|
31
|
+
resp_headers[name.to_sym] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Finalize the response object with values from `env`.
|
36
|
+
env[:response].finish(env)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Execute single request.
|
40
|
+
else
|
41
|
+
client = nil
|
42
|
+
block = lambda { request.send(http_method, request_config(env)) }
|
43
|
+
|
44
|
+
if !EM.reactor_running?
|
45
|
+
EM.run do
|
46
|
+
Fiber.new {
|
47
|
+
client = block.call
|
48
|
+
EM.stop
|
49
|
+
}.resume
|
50
|
+
end
|
51
|
+
else
|
52
|
+
client = block.call
|
53
|
+
end
|
54
|
+
|
55
|
+
save_response(env, client.response_header.status, client.response) do |resp_headers|
|
56
|
+
client.response_header.each do |name, value|
|
57
|
+
resp_headers[name.to_sym] = value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
@app.call env
|
63
|
+
rescue Errno::ECONNREFUSED
|
64
|
+
raise Error::ConnectionFailed, $!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
require 'faraday/adapter/em_synchrony/parallel_manager'
|
71
|
+
|
72
|
+
# add missing patch(), options() methods
|
73
|
+
EventMachine::HTTPMethods.module_eval do
|
74
|
+
([:patch, :options] - instance_methods).each do |type|
|
75
|
+
module_eval %[
|
76
|
+
def #{type}(options = {}, &blk)
|
77
|
+
f = Fiber.current
|
78
|
+
conn = setup_request(:#{type}, options, &blk)
|
79
|
+
if conn.error.nil?
|
80
|
+
conn.callback { f.resume(conn) }
|
81
|
+
conn.errback { f.resume(conn) }
|
82
|
+
Fiber.yield
|
83
|
+
else
|
84
|
+
conn
|
85
|
+
end
|
86
|
+
end
|
87
|
+
]
|
88
|
+
end
|
89
|
+
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
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Adapter
|
3
|
+
class Excon < Faraday::Adapter
|
4
|
+
dependency 'excon'
|
5
|
+
|
6
|
+
def initialize(app, connection_options = {})
|
7
|
+
@connection_options = connection_options
|
8
|
+
super(app)
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
super
|
13
|
+
|
14
|
+
opts = {}
|
15
|
+
if env[:url].scheme == 'https' && ssl = env[:ssl]
|
16
|
+
opts[:ssl_verify_peer] = !!ssl.fetch(:verify, true)
|
17
|
+
opts[:ssl_ca_path] = ssl[:ca_file] if ssl[:ca_file]
|
18
|
+
end
|
19
|
+
|
20
|
+
if ( req = env[:request] )
|
21
|
+
if req[:timeout]
|
22
|
+
opts[:read_timeout] = req[:timeout]
|
23
|
+
opts[:connect_timeout] = req[:timeout]
|
24
|
+
opts[:write_timeout] = req[:timeout]
|
25
|
+
end
|
26
|
+
|
27
|
+
if req[:open_timeout]
|
28
|
+
opts[:connect_timeout] = req[:open_timeout]
|
29
|
+
opts[:write_timeout] = req[:open_timeout]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
conn = ::Excon.new(env[:url].to_s, opts.merge(@connection_options))
|
34
|
+
|
35
|
+
resp = conn.request \
|
36
|
+
:method => env[:method].to_s.upcase,
|
37
|
+
:headers => env[:request_headers],
|
38
|
+
:body => read_body(env)
|
39
|
+
|
40
|
+
save_response(env, resp.status.to_i, resp.body, resp.headers)
|
41
|
+
|
42
|
+
@app.call env
|
43
|
+
rescue ::Excon::Errors::SocketError => err
|
44
|
+
if err.message =~ /\btimeout\b/
|
45
|
+
raise Error::TimeoutError, err
|
46
|
+
else
|
47
|
+
raise Error::ConnectionFailed, err
|
48
|
+
end
|
49
|
+
rescue ::Excon::Errors::Timeout => err
|
50
|
+
raise Error::TimeoutError, err
|
51
|
+
end
|
52
|
+
|
53
|
+
# TODO: support streaming requests
|
54
|
+
def read_body(env)
|
55
|
+
env[:body].respond_to?(:read) ? env[:body].read : env[:body]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Faraday
|
2
|
+
class Adapter
|
3
|
+
class HTTPClient < Faraday::Adapter
|
4
|
+
dependency 'httpclient'
|
5
|
+
|
6
|
+
def client
|
7
|
+
@client ||= ::HTTPClient.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
super
|
12
|
+
|
13
|
+
if req = env[:request]
|
14
|
+
if proxy = req[:proxy]
|
15
|
+
configure_proxy proxy
|
16
|
+
end
|
17
|
+
|
18
|
+
if bind = req[:bind]
|
19
|
+
configure_socket bind
|
20
|
+
end
|
21
|
+
|
22
|
+
configure_timeouts req
|
23
|
+
end
|
24
|
+
|
25
|
+
if env[:url].scheme == 'https' && ssl = env[:ssl]
|
26
|
+
configure_ssl ssl
|
27
|
+
end
|
28
|
+
|
29
|
+
# TODO Don't stream yet.
|
30
|
+
# https://github.com/nahi/httpclient/pull/90
|
31
|
+
env[:body] = env[:body].read if env[:body].respond_to? :read
|
32
|
+
|
33
|
+
resp = client.request env[:method], env[:url],
|
34
|
+
:body => env[:body],
|
35
|
+
:header => env[:request_headers]
|
36
|
+
|
37
|
+
save_response env, resp.status, resp.body, resp.headers
|
38
|
+
|
39
|
+
@app.call env
|
40
|
+
rescue ::HTTPClient::TimeoutError
|
41
|
+
raise Faraday::Error::TimeoutError, $!
|
42
|
+
end
|
43
|
+
|
44
|
+
def configure_socket(bind)
|
45
|
+
client.socket_local.host = bind[:host]
|
46
|
+
client.socket_local.port = bind[:port]
|
47
|
+
end
|
48
|
+
|
49
|
+
def configure_proxy(proxy)
|
50
|
+
client.proxy = proxy[:uri]
|
51
|
+
if proxy[:user] && proxy[:password]
|
52
|
+
client.set_proxy_auth proxy[:user], proxy[:password]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def configure_ssl(ssl)
|
57
|
+
ssl_config = client.ssl_config
|
58
|
+
|
59
|
+
ssl_config.add_trust_ca ssl[:ca_file] if ssl[:ca_file]
|
60
|
+
ssl_config.add_trust_ca ssl[:ca_path] if ssl[:ca_path]
|
61
|
+
ssl_config.cert_store = ssl[:cert_store] if ssl[:cert_store]
|
62
|
+
ssl_config.client_cert = ssl[:client_cert] if ssl[:client_cert]
|
63
|
+
ssl_config.client_key = ssl[:client_key] if ssl[:client_key]
|
64
|
+
ssl_config.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
|
65
|
+
ssl_config.verify_mode = ssl_verify_mode(ssl)
|
66
|
+
end
|
67
|
+
|
68
|
+
def configure_timeouts(req)
|
69
|
+
if req[:timeout]
|
70
|
+
client.connect_timeout = req[:timeout]
|
71
|
+
client.receive_timeout = req[:timeout]
|
72
|
+
client.send_timeout = req[:timeout]
|
73
|
+
end
|
74
|
+
|
75
|
+
if req[:open_timeout]
|
76
|
+
client.connect_timeout = req[:open_timeout]
|
77
|
+
client.send_timeout = req[:open_timeout]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def ssl_verify_mode(ssl)
|
82
|
+
ssl[:verify_mode] || begin
|
83
|
+
if ssl.fetch(:verify, true)
|
84
|
+
OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
85
|
+
else
|
86
|
+
OpenSSL::SSL::VERIFY_NONE
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
begin
|
2
|
+
require 'net/https'
|
3
|
+
rescue LoadError
|
4
|
+
warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support"
|
5
|
+
require 'net/http'
|
6
|
+
end
|
7
|
+
|
8
|
+
module Faraday
|
9
|
+
class Adapter
|
10
|
+
class NetHttp < Faraday::Adapter
|
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
|
+
]
|
22
|
+
|
23
|
+
NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
|
24
|
+
|
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]
|
29
|
+
|
30
|
+
req = env[:request]
|
31
|
+
http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
|
32
|
+
http.open_timeout = req[:open_timeout] if req[:open_timeout]
|
33
|
+
|
34
|
+
begin
|
35
|
+
http_response = perform_request(http, env)
|
36
|
+
rescue *NET_HTTP_EXCEPTIONS
|
37
|
+
raise Error::ConnectionFailed, $!
|
38
|
+
end
|
39
|
+
|
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
|
44
|
+
end
|
45
|
+
|
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
|
74
|
+
end
|
75
|
+
|
76
|
+
def net_http_connection(env)
|
77
|
+
if proxy = env[:request][:proxy]
|
78
|
+
Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
|
79
|
+
else
|
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
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'faraday/adapter/net_http'
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
class Adapter
|
5
|
+
# Experimental adapter for net-http-persistent
|
6
|
+
class NetHttpPersistent < NetHttp
|
7
|
+
dependency 'net/http/persistent'
|
8
|
+
|
9
|
+
# TODO: investigate is it safe to create a new Persistent instance for
|
10
|
+
# every request, or does it defy the purpose of persistent connections
|
11
|
+
def net_http_connection(env)
|
12
|
+
Net::HTTP::Persistent.new 'Faraday',
|
13
|
+
env[:request][:proxy] ? env[:request][:proxy][:uri] : nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def perform_request(http, env)
|
17
|
+
http.request env[:url], create_request(env)
|
18
|
+
rescue Net::HTTP::Persistent::Error => error
|
19
|
+
if error.message.include? 'Timeout::Error'
|
20
|
+
raise Faraday::Error::TimeoutError, error
|
21
|
+
else
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def configure_ssl(http, ssl)
|
27
|
+
http.verify_mode = ssl_verify_mode(ssl)
|
28
|
+
http.cert_store = ssl_cert_store(ssl)
|
29
|
+
|
30
|
+
http.certificate = ssl[:client_cert] if ssl[:client_cert]
|
31
|
+
http.private_key = ssl[:client_key] if ssl[:client_key]
|
32
|
+
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
|
33
|
+
http.ssl_version = ssl[:version] if ssl[:version]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|