faraday 0.6.0 → 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 +7 -0
- data/.document +6 -0
- data/CHANGELOG.md +15 -0
- data/CONTRIBUTING.md +36 -0
- data/Gemfile +25 -15
- data/{LICENSE → LICENSE.md} +1 -1
- data/README.md +187 -137
- data/Rakefile +38 -101
- data/faraday.gemspec +26 -81
- data/lib/faraday/adapter/em_http.rb +237 -0
- data/lib/faraday/adapter/em_http_ssl_patch.rb +56 -0
- data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +66 -0
- data/lib/faraday/adapter/em_synchrony.rb +65 -38
- data/lib/faraday/adapter/excon.rb +60 -9
- data/lib/faraday/adapter/httpclient.rb +106 -0
- data/lib/faraday/adapter/net_http.rb +89 -38
- data/lib/faraday/adapter/net_http_persistent.rb +47 -0
- data/lib/faraday/adapter/patron.rb +46 -9
- data/lib/faraday/adapter/rack.rb +58 -0
- data/lib/faraday/adapter/test.rb +68 -28
- data/lib/faraday/adapter/typhoeus.rb +94 -16
- data/lib/faraday/adapter.rb +33 -25
- data/lib/faraday/autoload.rb +85 -0
- data/lib/faraday/connection.rb +342 -134
- data/lib/faraday/error.rb +44 -29
- data/lib/faraday/middleware.rb +18 -10
- data/lib/faraday/options.rb +350 -0
- data/lib/faraday/parameters.rb +193 -0
- data/lib/faraday/rack_builder.rb +212 -0
- data/lib/faraday/request/authorization.rb +42 -0
- data/lib/faraday/request/basic_authentication.rb +13 -0
- data/lib/faraday/request/instrumentation.rb +36 -0
- data/lib/faraday/request/multipart.rb +15 -15
- data/lib/faraday/request/retry.rb +118 -0
- data/lib/faraday/request/token_authentication.rb +15 -0
- data/lib/faraday/request/url_encoded.rb +8 -9
- data/lib/faraday/request.rb +46 -45
- data/lib/faraday/response/logger.rb +4 -4
- data/lib/faraday/response/raise_error.rb +8 -3
- data/lib/faraday/response.rb +20 -25
- data/lib/faraday/upload_io.rb +51 -7
- data/lib/faraday/utils.rb +240 -44
- data/lib/faraday.rb +218 -38
- data/script/console +7 -0
- data/script/generate_certs +42 -0
- data/script/package +7 -0
- data/script/proxy-server +42 -0
- data/script/release +17 -0
- data/script/server +36 -0
- data/script/test +172 -0
- data/test/adapters/default_test.rb +14 -0
- data/test/adapters/em_http_test.rb +20 -0
- data/test/adapters/em_synchrony_test.rb +20 -0
- data/test/adapters/excon_test.rb +20 -0
- data/test/adapters/httpclient_test.rb +21 -0
- data/test/adapters/integration.rb +254 -0
- data/test/adapters/logger_test.rb +3 -3
- data/test/adapters/net_http_persistent_test.rb +20 -0
- data/test/adapters/net_http_test.rb +6 -25
- data/test/adapters/patron_test.rb +20 -0
- data/test/adapters/rack_test.rb +31 -0
- data/test/adapters/test_middleware_test.rb +74 -3
- data/test/adapters/typhoeus_test.rb +28 -0
- data/test/authentication_middleware_test.rb +65 -0
- data/test/composite_read_io_test.rb +111 -0
- data/test/connection_test.rb +381 -104
- data/test/env_test.rb +116 -40
- data/test/helper.rb +61 -25
- data/test/live_server.rb +55 -29
- data/test/middleware/instrumentation_test.rb +88 -0
- data/test/middleware/retry_test.rb +109 -0
- data/test/middleware_stack_test.rb +87 -5
- data/test/multibyte.txt +1 -0
- data/test/options_test.rb +252 -0
- data/test/request_middleware_test.rb +78 -21
- data/test/response_middleware_test.rb +32 -7
- data/test/strawberry.rb +2 -0
- data/test/utils_test.rb +58 -0
- metadata +116 -117
- data/lib/faraday/adapter/action_dispatch.rb +0 -30
- data/lib/faraday/builder.rb +0 -137
- data/lib/faraday/request/json.rb +0 -31
- data/test/adapters/live_test.rb +0 -186
|
@@ -0,0 +1,106 @@
|
|
|
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
|
+
rescue ::HTTPClient::BadResponseError => err
|
|
43
|
+
if err.message.include?('status 407')
|
|
44
|
+
raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
|
45
|
+
else
|
|
46
|
+
raise Faraday::Error::ClientError, $!
|
|
47
|
+
end
|
|
48
|
+
rescue Errno::ECONNREFUSED, EOFError
|
|
49
|
+
raise Faraday::Error::ConnectionFailed, $!
|
|
50
|
+
rescue => err
|
|
51
|
+
if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
|
|
52
|
+
raise Faraday::SSLError, err
|
|
53
|
+
else
|
|
54
|
+
raise
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def configure_socket(bind)
|
|
59
|
+
client.socket_local.host = bind[:host]
|
|
60
|
+
client.socket_local.port = bind[:port]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def configure_proxy(proxy)
|
|
64
|
+
client.proxy = proxy[:uri]
|
|
65
|
+
if proxy[:user] && proxy[:password]
|
|
66
|
+
client.set_proxy_auth proxy[:user], proxy[:password]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def configure_ssl(ssl)
|
|
71
|
+
ssl_config = client.ssl_config
|
|
72
|
+
|
|
73
|
+
ssl_config.add_trust_ca ssl[:ca_file] if ssl[:ca_file]
|
|
74
|
+
ssl_config.add_trust_ca ssl[:ca_path] if ssl[:ca_path]
|
|
75
|
+
ssl_config.cert_store = ssl[:cert_store] if ssl[:cert_store]
|
|
76
|
+
ssl_config.client_cert = ssl[:client_cert] if ssl[:client_cert]
|
|
77
|
+
ssl_config.client_key = ssl[:client_key] if ssl[:client_key]
|
|
78
|
+
ssl_config.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
|
|
79
|
+
ssl_config.verify_mode = ssl_verify_mode(ssl)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def configure_timeouts(req)
|
|
83
|
+
if req[:timeout]
|
|
84
|
+
client.connect_timeout = req[:timeout]
|
|
85
|
+
client.receive_timeout = req[:timeout]
|
|
86
|
+
client.send_timeout = req[:timeout]
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if req[:open_timeout]
|
|
90
|
+
client.connect_timeout = req[:open_timeout]
|
|
91
|
+
client.send_timeout = req[:open_timeout]
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def ssl_verify_mode(ssl)
|
|
96
|
+
ssl[:verify_mode] || begin
|
|
97
|
+
if ssl.fetch(:verify, true)
|
|
98
|
+
OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
|
99
|
+
else
|
|
100
|
+
OpenSSL::SSL::VERIFY_NONE
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -4,68 +4,119 @@ rescue LoadError
|
|
|
4
4
|
warn "Warning: no such file to load -- net/https. Make sure openssl is installed if you want ssl support"
|
|
5
5
|
require 'net/http'
|
|
6
6
|
end
|
|
7
|
+
require 'zlib'
|
|
7
8
|
|
|
8
9
|
module Faraday
|
|
9
10
|
class Adapter
|
|
10
11
|
class NetHttp < Faraday::Adapter
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
NET_HTTP_EXCEPTIONS = [
|
|
13
|
+
EOFError,
|
|
14
|
+
Errno::ECONNABORTED,
|
|
15
|
+
Errno::ECONNREFUSED,
|
|
16
|
+
Errno::ECONNRESET,
|
|
17
|
+
Errno::EHOSTUNREACH,
|
|
18
|
+
Errno::EINVAL,
|
|
19
|
+
Errno::ENETUNREACH,
|
|
20
|
+
Net::HTTPBadResponse,
|
|
21
|
+
Net::HTTPHeaderSyntaxError,
|
|
22
|
+
Net::ProtocolError,
|
|
23
|
+
SocketError,
|
|
24
|
+
Zlib::GzipFile::Error,
|
|
25
|
+
]
|
|
15
26
|
|
|
16
|
-
|
|
27
|
+
NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
|
|
17
28
|
|
|
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
|
|
29
|
+
def call(env)
|
|
30
|
+
super
|
|
31
|
+
http = net_http_connection(env)
|
|
32
|
+
configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
|
|
27
33
|
|
|
34
|
+
req = env[:request]
|
|
28
35
|
http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
|
|
29
36
|
http.open_timeout = req[:open_timeout] if req[:open_timeout]
|
|
30
37
|
|
|
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
38
|
begin
|
|
46
|
-
http_response =
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
http_response = perform_request(http, env)
|
|
40
|
+
rescue *NET_HTTP_EXCEPTIONS => err
|
|
41
|
+
if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
|
|
42
|
+
raise Faraday::SSLError, err
|
|
49
43
|
else
|
|
50
|
-
|
|
44
|
+
raise Error::ConnectionFailed, err
|
|
51
45
|
end
|
|
52
|
-
rescue Errno::ECONNREFUSED
|
|
53
|
-
raise Error::ConnectionFailed, $!
|
|
54
46
|
end
|
|
55
47
|
|
|
56
|
-
http_response.
|
|
57
|
-
|
|
48
|
+
save_response(env, http_response.code.to_i, http_response.body || '') do |response_headers|
|
|
49
|
+
http_response.each_header do |key, value|
|
|
50
|
+
response_headers[key] = value
|
|
51
|
+
end
|
|
58
52
|
end
|
|
59
|
-
env.update :status => http_response.code.to_i, :body => http_response.body
|
|
60
53
|
|
|
61
54
|
@app.call env
|
|
55
|
+
rescue Timeout::Error => err
|
|
56
|
+
raise Faraday::Error::TimeoutError, err
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def create_request(env)
|
|
60
|
+
request = Net::HTTPGenericRequest.new \
|
|
61
|
+
env[:method].to_s.upcase, # request method
|
|
62
|
+
!!env[:body], # is there request body
|
|
63
|
+
:head != env[:method], # is there response body
|
|
64
|
+
env[:url].request_uri, # request uri path
|
|
65
|
+
env[:request_headers] # request headers
|
|
66
|
+
|
|
67
|
+
if env[:body].respond_to?(:read)
|
|
68
|
+
request.body_stream = env[:body]
|
|
69
|
+
else
|
|
70
|
+
request.body = env[:body]
|
|
71
|
+
end
|
|
72
|
+
request
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def perform_request(http, env)
|
|
76
|
+
if :get == env[:method] and !env[:body]
|
|
77
|
+
# prefer `get` to `request` because the former handles gzip (ruby 1.9)
|
|
78
|
+
http.get env[:url].request_uri, env[:request_headers]
|
|
79
|
+
else
|
|
80
|
+
http.request create_request(env)
|
|
81
|
+
end
|
|
62
82
|
end
|
|
63
83
|
|
|
64
|
-
def
|
|
84
|
+
def net_http_connection(env)
|
|
65
85
|
if proxy = env[:request][:proxy]
|
|
66
86
|
Net::HTTP::Proxy(proxy[:uri].host, proxy[:uri].port, proxy[:user], proxy[:password])
|
|
67
87
|
else
|
|
68
88
|
Net::HTTP
|
|
89
|
+
end.new(env[:url].host, env[:url].port)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def configure_ssl(http, ssl)
|
|
93
|
+
http.use_ssl = true
|
|
94
|
+
http.verify_mode = ssl_verify_mode(ssl)
|
|
95
|
+
http.cert_store = ssl_cert_store(ssl)
|
|
96
|
+
|
|
97
|
+
http.cert = ssl[:client_cert] if ssl[:client_cert]
|
|
98
|
+
http.key = ssl[:client_key] if ssl[:client_key]
|
|
99
|
+
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
|
|
100
|
+
http.ca_path = ssl[:ca_path] if ssl[:ca_path]
|
|
101
|
+
http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
|
|
102
|
+
http.ssl_version = ssl[:version] if ssl[:version]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def ssl_cert_store(ssl)
|
|
106
|
+
return ssl[:cert_store] if ssl[:cert_store]
|
|
107
|
+
# Use the default cert store by default, i.e. system ca certs
|
|
108
|
+
cert_store = OpenSSL::X509::Store.new
|
|
109
|
+
cert_store.set_default_paths
|
|
110
|
+
cert_store
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def ssl_verify_mode(ssl)
|
|
114
|
+
ssl[:verify_mode] || begin
|
|
115
|
+
if ssl.fetch(:verify, true)
|
|
116
|
+
OpenSSL::SSL::VERIFY_PEER
|
|
117
|
+
else
|
|
118
|
+
OpenSSL::SSL::VERIFY_NONE
|
|
119
|
+
end
|
|
69
120
|
end
|
|
70
121
|
end
|
|
71
122
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Rely on autoloading instead of explicit require; helps avoid the "already
|
|
2
|
+
# initialized constant" warning on Ruby 1.8.7 when NetHttp is refereced below.
|
|
3
|
+
# require 'faraday/adapter/net_http'
|
|
4
|
+
|
|
5
|
+
module Faraday
|
|
6
|
+
class Adapter
|
|
7
|
+
# Experimental adapter for net-http-persistent
|
|
8
|
+
class NetHttpPersistent < NetHttp
|
|
9
|
+
dependency 'net/http/persistent'
|
|
10
|
+
|
|
11
|
+
def net_http_connection(env)
|
|
12
|
+
if proxy = env[:request][:proxy]
|
|
13
|
+
proxy_uri = ::URI::HTTP === proxy[:uri] ? proxy[:uri].dup : ::URI.parse(proxy[:uri].to_s)
|
|
14
|
+
proxy_uri.user = proxy_uri.password = nil
|
|
15
|
+
# awful patch for net-http-persistent 2.8 not unescaping user/password
|
|
16
|
+
(class << proxy_uri; self; end).class_eval do
|
|
17
|
+
define_method(:user) { proxy[:user] }
|
|
18
|
+
define_method(:password) { proxy[:password] }
|
|
19
|
+
end if proxy[:user]
|
|
20
|
+
end
|
|
21
|
+
Net::HTTP::Persistent.new 'Faraday', proxy_uri
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def perform_request(http, env)
|
|
25
|
+
http.request env[:url], create_request(env)
|
|
26
|
+
rescue Net::HTTP::Persistent::Error => error
|
|
27
|
+
if error.message.include? 'Timeout'
|
|
28
|
+
raise Faraday::Error::TimeoutError, error
|
|
29
|
+
elsif error.message.include? 'connection refused'
|
|
30
|
+
raise Faraday::Error::ConnectionFailed, error
|
|
31
|
+
else
|
|
32
|
+
raise
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def configure_ssl(http, ssl)
|
|
37
|
+
http.verify_mode = ssl_verify_mode(ssl)
|
|
38
|
+
http.cert_store = ssl_cert_store(ssl)
|
|
39
|
+
|
|
40
|
+
http.certificate = ssl[:client_cert] if ssl[:client_cert]
|
|
41
|
+
http.private_key = ssl[:client_key] if ssl[:client_key]
|
|
42
|
+
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
|
|
43
|
+
http.ssl_version = ssl[:version] if ssl[:version]
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -3,28 +3,65 @@ module Faraday
|
|
|
3
3
|
class Patron < Faraday::Adapter
|
|
4
4
|
dependency 'patron'
|
|
5
5
|
|
|
6
|
+
def initialize(app, &block)
|
|
7
|
+
super(app)
|
|
8
|
+
@block = block
|
|
9
|
+
end
|
|
10
|
+
|
|
6
11
|
def call(env)
|
|
7
12
|
super
|
|
8
13
|
|
|
9
14
|
# TODO: support streaming requests
|
|
10
15
|
env[:body] = env[:body].read if env[:body].respond_to? :read
|
|
11
16
|
|
|
12
|
-
session =
|
|
17
|
+
session = @session ||= create_session
|
|
13
18
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
if req = env[:request]
|
|
20
|
+
session.timeout = session.connect_timeout = req[:timeout] if req[:timeout]
|
|
21
|
+
session.connect_timeout = req[:open_timeout] if req[:open_timeout]
|
|
22
|
+
|
|
23
|
+
if proxy = req[:proxy]
|
|
24
|
+
proxy_uri = proxy[:uri].dup
|
|
25
|
+
proxy_uri.user = proxy[:user] && Utils.escape(proxy[:user]).gsub('+', '%20')
|
|
26
|
+
proxy_uri.password = proxy[:password] && Utils.escape(proxy[:password]).gsub('+', '%20')
|
|
27
|
+
session.proxy = proxy_uri.to_s
|
|
19
28
|
end
|
|
20
|
-
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
response = begin
|
|
32
|
+
data = env[:body] ? env[:body].to_s : nil
|
|
33
|
+
session.request(env[:method], env[:url].to_s, env[:request_headers], :data => data)
|
|
34
|
+
rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
|
|
21
35
|
raise Error::ConnectionFailed, $!
|
|
22
36
|
end
|
|
23
37
|
|
|
24
|
-
env
|
|
25
|
-
response_headers(env).update response.headers
|
|
38
|
+
save_response(env, response.status, response.body, response.headers)
|
|
26
39
|
|
|
27
40
|
@app.call env
|
|
41
|
+
rescue ::Patron::TimeoutError => err
|
|
42
|
+
raise Faraday::Error::TimeoutError, err
|
|
43
|
+
rescue ::Patron::Error => err
|
|
44
|
+
if err.message.include?("code 407")
|
|
45
|
+
raise Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
|
46
|
+
else
|
|
47
|
+
raise Error::ConnectionFailed, err
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if loaded? && defined?(::Patron::Request::VALID_ACTIONS)
|
|
52
|
+
# HAX: helps but doesn't work completely
|
|
53
|
+
# https://github.com/toland/patron/issues/34
|
|
54
|
+
::Patron::Request::VALID_ACTIONS.tap do |actions|
|
|
55
|
+
actions << :patch unless actions.include? :patch
|
|
56
|
+
actions << :options unless actions.include? :options
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def create_session
|
|
61
|
+
session = ::Patron::Session.new
|
|
62
|
+
session.insecure = true
|
|
63
|
+
@block.call(session) if @block
|
|
64
|
+
session
|
|
28
65
|
end
|
|
29
66
|
end
|
|
30
67
|
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
module Faraday
|
|
2
|
+
class Adapter
|
|
3
|
+
# Sends requests to a Rack app.
|
|
4
|
+
#
|
|
5
|
+
# Examples
|
|
6
|
+
#
|
|
7
|
+
# class MyRackApp
|
|
8
|
+
# def call(env)
|
|
9
|
+
# [200, {'Content-Type' => 'text/html'}, ["hello world"]]
|
|
10
|
+
# end
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Faraday.new do |conn|
|
|
14
|
+
# conn.adapter :rack, MyRackApp.new
|
|
15
|
+
# end
|
|
16
|
+
class Rack < Faraday::Adapter
|
|
17
|
+
dependency 'rack/test'
|
|
18
|
+
|
|
19
|
+
# not prefixed with "HTTP_"
|
|
20
|
+
SPECIAL_HEADERS = %w[ CONTENT_LENGTH CONTENT_TYPE ]
|
|
21
|
+
|
|
22
|
+
def initialize(faraday_app, rack_app)
|
|
23
|
+
super(faraday_app)
|
|
24
|
+
mock_session = ::Rack::MockSession.new(rack_app)
|
|
25
|
+
@session = ::Rack::Test::Session.new(mock_session)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def call(env)
|
|
29
|
+
super
|
|
30
|
+
rack_env = {
|
|
31
|
+
:method => env[:method],
|
|
32
|
+
:input => env[:body].respond_to?(:read) ? env[:body].read : env[:body],
|
|
33
|
+
'rack.url_scheme' => env[:url].scheme
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
env[:request_headers].each do |name, value|
|
|
37
|
+
name = name.upcase.tr('-', '_')
|
|
38
|
+
name = "HTTP_#{name}" unless SPECIAL_HEADERS.include? name
|
|
39
|
+
rack_env[name] = value
|
|
40
|
+
end if env[:request_headers]
|
|
41
|
+
|
|
42
|
+
timeout = env[:request][:timeout] || env[:request][:open_timeout]
|
|
43
|
+
response = if timeout
|
|
44
|
+
Timer.timeout(timeout, Faraday::Error::TimeoutError) { execute_request(env, rack_env) }
|
|
45
|
+
else
|
|
46
|
+
execute_request(env, rack_env)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
save_response(env, response.status, response.body, response.headers)
|
|
50
|
+
@app.call env
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def execute_request(env, rack_env)
|
|
54
|
+
@session.request(env[:url].to_s, rack_env)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
data/lib/faraday/adapter/test.rb
CHANGED
|
@@ -14,50 +14,59 @@ module Faraday
|
|
|
14
14
|
class Test < Faraday::Adapter
|
|
15
15
|
attr_accessor :stubs
|
|
16
16
|
|
|
17
|
-
def self.loaded?() false end
|
|
18
|
-
|
|
19
17
|
class Stubs
|
|
18
|
+
class NotFound < StandardError
|
|
19
|
+
end
|
|
20
|
+
|
|
20
21
|
def initialize
|
|
21
22
|
# {:get => [Stub, Stub]}
|
|
22
23
|
@stack, @consumed = {}, {}
|
|
23
|
-
yield
|
|
24
|
+
yield(self) if block_given?
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def empty?
|
|
27
28
|
@stack.empty?
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
def match(request_method, path, body)
|
|
31
|
+
def match(request_method, path, headers, body)
|
|
31
32
|
return false if !@stack.key?(request_method)
|
|
32
33
|
stack = @stack[request_method]
|
|
33
34
|
consumed = (@consumed[request_method] ||= [])
|
|
34
35
|
|
|
35
|
-
if stub = matches?(stack, path, body)
|
|
36
|
+
if stub = matches?(stack, path, headers, body)
|
|
36
37
|
consumed << stack.delete(stub)
|
|
37
38
|
stub
|
|
38
39
|
else
|
|
39
|
-
matches?(consumed, path, body)
|
|
40
|
+
matches?(consumed, path, headers, body)
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
|
|
43
|
-
def get(path, &block)
|
|
44
|
-
new_stub(:get, path, &block)
|
|
44
|
+
def get(path, headers = {}, &block)
|
|
45
|
+
new_stub(:get, path, headers, &block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def head(path, headers = {}, &block)
|
|
49
|
+
new_stub(:head, path, headers, &block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def post(path, body=nil, headers = {}, &block)
|
|
53
|
+
new_stub(:post, path, headers, body, &block)
|
|
45
54
|
end
|
|
46
55
|
|
|
47
|
-
def
|
|
48
|
-
new_stub(:
|
|
56
|
+
def put(path, body=nil, headers = {}, &block)
|
|
57
|
+
new_stub(:put, path, headers, body, &block)
|
|
49
58
|
end
|
|
50
59
|
|
|
51
|
-
def
|
|
52
|
-
new_stub(:
|
|
60
|
+
def patch(path, body=nil, headers = {}, &block)
|
|
61
|
+
new_stub(:patch, path, headers, body, &block)
|
|
53
62
|
end
|
|
54
63
|
|
|
55
|
-
def
|
|
56
|
-
new_stub(:
|
|
64
|
+
def delete(path, headers = {}, &block)
|
|
65
|
+
new_stub(:delete, path, headers, &block)
|
|
57
66
|
end
|
|
58
67
|
|
|
59
|
-
def
|
|
60
|
-
new_stub(:
|
|
68
|
+
def options(path, headers = {}, &block)
|
|
69
|
+
new_stub(:options, path, headers, &block)
|
|
61
70
|
end
|
|
62
71
|
|
|
63
72
|
# Raises an error if any of the stubbed calls have not been made.
|
|
@@ -75,18 +84,46 @@ module Faraday
|
|
|
75
84
|
|
|
76
85
|
protected
|
|
77
86
|
|
|
78
|
-
def new_stub(request_method, path, body=nil, &block)
|
|
79
|
-
|
|
87
|
+
def new_stub(request_method, path, headers = {}, body=nil, &block)
|
|
88
|
+
normalized_path = Faraday::Utils.normalize_path(path)
|
|
89
|
+
(@stack[request_method] ||= []) << Stub.new(normalized_path, headers, body, block)
|
|
80
90
|
end
|
|
81
91
|
|
|
82
|
-
def matches?(stack, path, body)
|
|
83
|
-
stack.detect { |stub| stub.matches?(path, body) }
|
|
92
|
+
def matches?(stack, path, headers, body)
|
|
93
|
+
stack.detect { |stub| stub.matches?(path, headers, body) }
|
|
84
94
|
end
|
|
85
95
|
end
|
|
86
96
|
|
|
87
|
-
class Stub < Struct.new(:path, :body, :block)
|
|
88
|
-
def
|
|
89
|
-
|
|
97
|
+
class Stub < Struct.new(:path, :params, :headers, :body, :block)
|
|
98
|
+
def initialize(full, headers, body, block)
|
|
99
|
+
path, query = full.split('?')
|
|
100
|
+
params = query ?
|
|
101
|
+
Faraday::Utils.parse_nested_query(query) :
|
|
102
|
+
{}
|
|
103
|
+
super(path, params, headers, body, block)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def matches?(request_uri, request_headers, request_body)
|
|
107
|
+
request_path, request_query = request_uri.split('?')
|
|
108
|
+
request_params = request_query ?
|
|
109
|
+
Faraday::Utils.parse_nested_query(request_query) :
|
|
110
|
+
{}
|
|
111
|
+
request_path == path &&
|
|
112
|
+
params_match?(request_params) &&
|
|
113
|
+
(body.to_s.size.zero? || request_body == body) &&
|
|
114
|
+
headers_match?(request_headers)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def params_match?(request_params)
|
|
118
|
+
params.keys.all? do |key|
|
|
119
|
+
request_params[key] == params[key]
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def headers_match?(request_headers)
|
|
124
|
+
headers.keys.all? do |key|
|
|
125
|
+
request_headers[key] == headers[key]
|
|
126
|
+
end
|
|
90
127
|
end
|
|
91
128
|
|
|
92
129
|
def to_s
|
|
@@ -101,19 +138,22 @@ module Faraday
|
|
|
101
138
|
end
|
|
102
139
|
|
|
103
140
|
def configure
|
|
104
|
-
yield
|
|
141
|
+
yield(stubs)
|
|
105
142
|
end
|
|
106
143
|
|
|
107
144
|
def call(env)
|
|
108
145
|
super
|
|
109
146
|
normalized_path = Faraday::Utils.normalize_path(env[:url])
|
|
147
|
+
params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder
|
|
110
148
|
|
|
111
|
-
if stub = stubs.match(env[:method], normalized_path, env[:body])
|
|
149
|
+
if stub = stubs.match(env[:method], normalized_path, env.request_headers, env[:body])
|
|
150
|
+
env[:params] = (query = env[:url].query) ?
|
|
151
|
+
params_encoder.decode(query) :
|
|
152
|
+
{}
|
|
112
153
|
status, headers, body = stub.block.call(env)
|
|
113
|
-
env
|
|
114
|
-
response_headers(env).update headers
|
|
154
|
+
save_response(env, status, body, headers)
|
|
115
155
|
else
|
|
116
|
-
raise "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
|
|
156
|
+
raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
|
|
117
157
|
end
|
|
118
158
|
@app.call(env)
|
|
119
159
|
end
|