faraday 0.9.2 → 0.17.6
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/CHANGELOG.md +232 -0
- data/LICENSE.md +1 -1
- data/README.md +168 -18
- data/Rakefile +13 -0
- data/lib/faraday/adapter/em_http.rb +17 -11
- data/lib/faraday/adapter/em_http_ssl_patch.rb +1 -1
- data/lib/faraday/adapter/em_synchrony.rb +13 -7
- data/lib/faraday/adapter/excon.rb +12 -11
- data/lib/faraday/adapter/httpclient.rb +21 -10
- data/lib/faraday/adapter/net_http.rb +36 -13
- data/lib/faraday/adapter/net_http_persistent.rb +36 -17
- data/lib/faraday/adapter/patron.rb +36 -19
- data/lib/faraday/adapter/rack.rb +1 -1
- data/lib/faraday/adapter/test.rb +79 -28
- data/lib/faraday/adapter/typhoeus.rb +4 -115
- data/lib/faraday/adapter.rb +10 -1
- data/lib/faraday/autoload.rb +1 -1
- data/lib/faraday/connection.rb +64 -17
- data/lib/faraday/deprecate.rb +109 -0
- data/lib/faraday/error.rb +132 -27
- data/lib/faraday/options.rb +36 -22
- data/lib/faraday/parameters.rb +2 -1
- data/lib/faraday/rack_builder.rb +26 -2
- data/lib/faraday/request/authorization.rb +1 -2
- data/lib/faraday/request/multipart.rb +7 -2
- data/lib/faraday/request/retry.rb +77 -18
- data/lib/faraday/request.rb +24 -0
- data/lib/faraday/response/logger.rb +29 -8
- data/lib/faraday/response/raise_error.rb +7 -3
- data/lib/faraday/response.rb +7 -3
- data/lib/faraday/upload_io.rb +16 -6
- data/lib/faraday/utils.rb +19 -2
- data/lib/faraday.rb +15 -36
- data/spec/faraday/deprecate_spec.rb +147 -0
- data/spec/faraday/error_spec.rb +102 -0
- data/spec/faraday/response/raise_error_spec.rb +106 -0
- data/spec/spec_helper.rb +105 -0
- data/test/adapters/default_test.rb +14 -0
- data/test/adapters/em_http_test.rb +30 -0
- data/test/adapters/em_synchrony_test.rb +32 -0
- data/test/adapters/excon_test.rb +30 -0
- data/test/adapters/httpclient_test.rb +34 -0
- data/test/adapters/integration.rb +263 -0
- data/test/adapters/logger_test.rb +136 -0
- data/test/adapters/net_http_persistent_test.rb +114 -0
- data/test/adapters/net_http_test.rb +79 -0
- data/test/adapters/patron_test.rb +40 -0
- data/test/adapters/rack_test.rb +38 -0
- data/test/adapters/test_middleware_test.rb +157 -0
- data/test/adapters/typhoeus_test.rb +38 -0
- data/test/authentication_middleware_test.rb +65 -0
- data/test/composite_read_io_test.rb +109 -0
- data/test/connection_test.rb +738 -0
- data/test/env_test.rb +268 -0
- data/test/helper.rb +75 -0
- data/test/live_server.rb +67 -0
- data/test/middleware/instrumentation_test.rb +88 -0
- data/test/middleware/retry_test.rb +282 -0
- data/test/middleware_stack_test.rb +260 -0
- data/test/multibyte.txt +1 -0
- data/test/options_test.rb +333 -0
- data/test/parameters_test.rb +157 -0
- data/test/request_middleware_test.rb +126 -0
- data/test/response_middleware_test.rb +72 -0
- data/test/strawberry.rb +2 -0
- data/test/utils_test.rb +98 -0
- metadata +81 -63
@@ -3,11 +3,6 @@ module Faraday
|
|
3
3
|
class Excon < Faraday::Adapter
|
4
4
|
dependency 'excon'
|
5
5
|
|
6
|
-
def initialize(app, connection_options = {})
|
7
|
-
@connection_options = connection_options
|
8
|
-
super(app)
|
9
|
-
end
|
10
|
-
|
11
6
|
def call(env)
|
12
7
|
super
|
13
8
|
|
@@ -20,6 +15,9 @@ module Faraday
|
|
20
15
|
opts[:client_key] = ssl[:client_key] if ssl[:client_key]
|
21
16
|
opts[:certificate] = ssl[:certificate] if ssl[:certificate]
|
22
17
|
opts[:private_key] = ssl[:private_key] if ssl[:private_key]
|
18
|
+
opts[:ssl_version] = ssl[:version] if ssl[:version]
|
19
|
+
opts[:ssl_min_version] = ssl[:min_version] if ssl[:min_version]
|
20
|
+
opts[:ssl_max_version] = ssl[:max_version] if ssl[:max_version]
|
23
21
|
|
24
22
|
# https://github.com/geemus/excon/issues/106
|
25
23
|
# https://github.com/jruby/jruby-ossl/issues/19
|
@@ -35,7 +33,6 @@ module Faraday
|
|
35
33
|
|
36
34
|
if req[:open_timeout]
|
37
35
|
opts[:connect_timeout] = req[:open_timeout]
|
38
|
-
opts[:write_timeout] = req[:open_timeout]
|
39
36
|
end
|
40
37
|
|
41
38
|
if req[:proxy]
|
@@ -50,26 +47,30 @@ module Faraday
|
|
50
47
|
end
|
51
48
|
end
|
52
49
|
|
53
|
-
conn =
|
50
|
+
conn = create_connection(env, opts)
|
54
51
|
|
55
52
|
resp = conn.request \
|
56
53
|
:method => env[:method].to_s.upcase,
|
57
54
|
:headers => env[:request_headers],
|
58
55
|
:body => read_body(env)
|
59
56
|
|
60
|
-
save_response(env, resp.status.to_i, resp.body, resp.headers)
|
57
|
+
save_response(env, resp.status.to_i, resp.body, resp.headers, resp.reason_phrase)
|
61
58
|
|
62
59
|
@app.call env
|
63
60
|
rescue ::Excon::Errors::SocketError => err
|
64
61
|
if err.message =~ /\btimeout\b/
|
65
|
-
raise
|
62
|
+
raise Faraday::TimeoutError, err
|
66
63
|
elsif err.message =~ /\bcertificate\b/
|
67
64
|
raise Faraday::SSLError, err
|
68
65
|
else
|
69
|
-
raise
|
66
|
+
raise Faraday::ConnectionFailed, err
|
70
67
|
end
|
71
68
|
rescue ::Excon::Errors::Timeout => err
|
72
|
-
raise
|
69
|
+
raise Faraday::TimeoutError, err
|
70
|
+
end
|
71
|
+
|
72
|
+
def create_connection(env, opts)
|
73
|
+
::Excon.new(env[:url].to_s, opts.merge(@connection_options))
|
73
74
|
end
|
74
75
|
|
75
76
|
# TODO: support streaming requests
|
@@ -29,6 +29,8 @@ module Faraday
|
|
29
29
|
configure_ssl ssl
|
30
30
|
end
|
31
31
|
|
32
|
+
configure_client
|
33
|
+
|
32
34
|
# TODO Don't stream yet.
|
33
35
|
# https://github.com/nahi/httpclient/pull/90
|
34
36
|
env[:body] = env[:body].read if env[:body].respond_to? :read
|
@@ -37,19 +39,19 @@ module Faraday
|
|
37
39
|
:body => env[:body],
|
38
40
|
:header => env[:request_headers]
|
39
41
|
|
40
|
-
save_response env, resp.status, resp.body, resp.headers
|
42
|
+
save_response env, resp.status, resp.body, resp.headers, resp.reason
|
41
43
|
|
42
44
|
@app.call env
|
43
45
|
rescue ::HTTPClient::TimeoutError, Errno::ETIMEDOUT
|
44
|
-
raise Faraday::
|
46
|
+
raise Faraday::TimeoutError, $!
|
45
47
|
rescue ::HTTPClient::BadResponseError => err
|
46
48
|
if err.message.include?('status 407')
|
47
|
-
raise Faraday::
|
49
|
+
raise Faraday::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
48
50
|
else
|
49
|
-
raise Faraday::
|
51
|
+
raise Faraday::ClientError, $!
|
50
52
|
end
|
51
|
-
rescue Errno::ECONNREFUSED,
|
52
|
-
raise Faraday::
|
53
|
+
rescue Errno::ECONNREFUSED, IOError, SocketError
|
54
|
+
raise Faraday::ConnectionFailed, $!
|
53
55
|
rescue => err
|
54
56
|
if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
|
55
57
|
raise Faraday::SSLError, err
|
@@ -95,12 +97,21 @@ module Faraday
|
|
95
97
|
end
|
96
98
|
end
|
97
99
|
|
100
|
+
def configure_client
|
101
|
+
@config_block.call(client) if @config_block
|
102
|
+
end
|
103
|
+
|
98
104
|
def ssl_cert_store(ssl)
|
99
105
|
return ssl[:cert_store] if ssl[:cert_store]
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
cert_store
|
106
|
+
# Memoize the cert store so that the same one is passed to
|
107
|
+
# HTTPClient each time, to avoid resyncing SSL sesions when
|
108
|
+
# it's changed
|
109
|
+
@cert_store ||= begin
|
110
|
+
# Use the default cert store by default, i.e. system ca certs
|
111
|
+
cert_store = OpenSSL::X509::Store.new
|
112
|
+
cert_store.set_default_paths
|
113
|
+
cert_store
|
114
|
+
end
|
104
115
|
end
|
105
116
|
|
106
117
|
def ssl_verify_mode(ssl)
|
@@ -10,13 +10,15 @@ module Faraday
|
|
10
10
|
class Adapter
|
11
11
|
class NetHttp < Faraday::Adapter
|
12
12
|
NET_HTTP_EXCEPTIONS = [
|
13
|
-
|
13
|
+
IOError,
|
14
|
+
Errno::EADDRNOTAVAIL,
|
14
15
|
Errno::ECONNABORTED,
|
15
16
|
Errno::ECONNREFUSED,
|
16
17
|
Errno::ECONNRESET,
|
17
18
|
Errno::EHOSTUNREACH,
|
18
19
|
Errno::EINVAL,
|
19
20
|
Errno::ENETUNREACH,
|
21
|
+
Errno::EPIPE,
|
20
22
|
Net::HTTPBadResponse,
|
21
23
|
Net::HTTPHeaderSyntaxError,
|
22
24
|
Net::ProtocolError,
|
@@ -27,14 +29,16 @@ module Faraday
|
|
27
29
|
NET_HTTP_EXCEPTIONS << OpenSSL::SSL::SSLError if defined?(OpenSSL)
|
28
30
|
NET_HTTP_EXCEPTIONS << Net::OpenTimeout if defined?(Net::OpenTimeout)
|
29
31
|
|
32
|
+
def initialize(app = nil, opts = {}, &block)
|
33
|
+
@cert_store = nil
|
34
|
+
super(app, opts, &block)
|
35
|
+
end
|
36
|
+
|
30
37
|
def call(env)
|
31
38
|
super
|
32
39
|
with_net_http_connection(env) do |http|
|
33
40
|
configure_ssl(http, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
|
34
|
-
|
35
|
-
req = env[:request]
|
36
|
-
http.read_timeout = http.open_timeout = req[:timeout] if req[:timeout]
|
37
|
-
http.open_timeout = req[:open_timeout] if req[:open_timeout]
|
41
|
+
configure_request(http, env[:request])
|
38
42
|
|
39
43
|
begin
|
40
44
|
http_response = perform_request(http, env)
|
@@ -42,11 +46,11 @@ module Faraday
|
|
42
46
|
if defined?(OpenSSL) && OpenSSL::SSL::SSLError === err
|
43
47
|
raise Faraday::SSLError, err
|
44
48
|
else
|
45
|
-
raise
|
49
|
+
raise Faraday::ConnectionFailed, err
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
49
|
-
save_response(env, http_response.code.to_i, http_response.body || '') do |response_headers|
|
53
|
+
save_response(env, http_response.code.to_i, http_response.body || '', nil, http_response.message) do |response_headers|
|
50
54
|
http_response.each_header do |key, value|
|
51
55
|
response_headers[key] = value
|
52
56
|
end
|
@@ -55,9 +59,11 @@ module Faraday
|
|
55
59
|
|
56
60
|
@app.call env
|
57
61
|
rescue Timeout::Error, Errno::ETIMEDOUT => err
|
58
|
-
raise Faraday::
|
62
|
+
raise Faraday::TimeoutError, err
|
59
63
|
end
|
60
64
|
|
65
|
+
private
|
66
|
+
|
61
67
|
def create_request(env)
|
62
68
|
request = Net::HTTPGenericRequest.new \
|
63
69
|
env[:method].to_s.upcase, # request method
|
@@ -89,10 +95,10 @@ module Faraday
|
|
89
95
|
|
90
96
|
def net_http_connection(env)
|
91
97
|
if proxy = env[:request][:proxy]
|
92
|
-
Net::HTTP::Proxy(proxy[:uri].
|
98
|
+
Net::HTTP::Proxy(proxy[:uri].hostname, proxy[:uri].port, proxy[:user], proxy[:password])
|
93
99
|
else
|
94
100
|
Net::HTTP
|
95
|
-
end.new(env[:url].
|
101
|
+
end.new(env[:url].hostname, env[:url].port || (env[:url].scheme == 'https' ? 443 : 80))
|
96
102
|
end
|
97
103
|
|
98
104
|
def configure_ssl(http, ssl)
|
@@ -106,14 +112,31 @@ module Faraday
|
|
106
112
|
http.ca_path = ssl[:ca_path] if ssl[:ca_path]
|
107
113
|
http.verify_depth = ssl[:verify_depth] if ssl[:verify_depth]
|
108
114
|
http.ssl_version = ssl[:version] if ssl[:version]
|
115
|
+
http.min_version = ssl[:min_version] if ssl[:min_version]
|
116
|
+
http.max_version = ssl[:max_version] if ssl[:max_version]
|
117
|
+
end
|
118
|
+
|
119
|
+
def configure_request(http, req)
|
120
|
+
if req[:timeout]
|
121
|
+
http.read_timeout = req[:timeout]
|
122
|
+
http.open_timeout = req[:timeout]
|
123
|
+
http.write_timeout = req[:timeout] if http.respond_to?(:write_timeout=)
|
124
|
+
end
|
125
|
+
http.open_timeout = req[:open_timeout] if req[:open_timeout]
|
126
|
+
http.write_timeout = req[:write_timeout] if req[:write_timeout] && http.respond_to?(:write_timeout=)
|
127
|
+
# Only set if Net::Http supports it, since Ruby 2.5.
|
128
|
+
http.max_retries = 0 if http.respond_to?(:max_retries=)
|
129
|
+
|
130
|
+
@config_block.call(http) if @config_block
|
109
131
|
end
|
110
132
|
|
111
133
|
def ssl_cert_store(ssl)
|
112
134
|
return ssl[:cert_store] if ssl[:cert_store]
|
135
|
+
return @cert_store if @cert_store
|
113
136
|
# Use the default cert store by default, i.e. system ca certs
|
114
|
-
cert_store = OpenSSL::X509::Store.new
|
115
|
-
cert_store.set_default_paths
|
116
|
-
cert_store
|
137
|
+
@cert_store = OpenSSL::X509::Store.new
|
138
|
+
@cert_store.set_default_paths
|
139
|
+
@cert_store
|
117
140
|
end
|
118
141
|
|
119
142
|
def ssl_verify_mode(ssl)
|
@@ -1,14 +1,28 @@
|
|
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
1
|
module Faraday
|
6
2
|
class Adapter
|
7
3
|
class NetHttpPersistent < NetHttp
|
8
4
|
dependency 'net/http/persistent'
|
9
5
|
|
10
|
-
|
11
|
-
|
6
|
+
private
|
7
|
+
|
8
|
+
def net_http_connection(env)
|
9
|
+
@cached_connection ||=
|
10
|
+
if Net::HTTP::Persistent.instance_method(:initialize).parameters.first == [:key, :name]
|
11
|
+
options = {name: 'Faraday'}
|
12
|
+
options[:pool_size] = @connection_options[:pool_size] if @connection_options.key?(:pool_size)
|
13
|
+
Net::HTTP::Persistent.new(**options)
|
14
|
+
else
|
15
|
+
Net::HTTP::Persistent.new('Faraday')
|
16
|
+
end
|
17
|
+
|
18
|
+
proxy_uri = proxy_uri(env)
|
19
|
+
@cached_connection.proxy = proxy_uri if @cached_connection.proxy_uri != proxy_uri
|
20
|
+
@cached_connection
|
21
|
+
end
|
22
|
+
|
23
|
+
def proxy_uri(env)
|
24
|
+
proxy_uri = nil
|
25
|
+
if (proxy = env[:request][:proxy])
|
12
26
|
proxy_uri = ::URI::HTTP === proxy[:uri] ? proxy[:uri].dup : ::URI.parse(proxy[:uri].to_s)
|
13
27
|
proxy_uri.user = proxy_uri.password = nil
|
14
28
|
# awful patch for net-http-persistent 2.8 not unescaping user/password
|
@@ -17,32 +31,37 @@ module Faraday
|
|
17
31
|
define_method(:password) { proxy[:password] }
|
18
32
|
end if proxy[:user]
|
19
33
|
end
|
20
|
-
|
21
|
-
yield Net::HTTP::Persistent.new 'Faraday', proxy_uri
|
34
|
+
proxy_uri
|
22
35
|
end
|
23
36
|
|
24
37
|
def perform_request(http, env)
|
25
38
|
http.request env[:url], create_request(env)
|
26
39
|
rescue Errno::ETIMEDOUT => error
|
27
|
-
raise Faraday::
|
40
|
+
raise Faraday::TimeoutError, error
|
28
41
|
rescue Net::HTTP::Persistent::Error => error
|
29
42
|
if error.message.include? 'Timeout'
|
30
|
-
raise Faraday::
|
43
|
+
raise Faraday::TimeoutError, error
|
31
44
|
elsif error.message.include? 'connection refused'
|
32
|
-
raise Faraday::
|
45
|
+
raise Faraday::ConnectionFailed, error
|
33
46
|
else
|
34
47
|
raise
|
35
48
|
end
|
36
49
|
end
|
37
50
|
|
38
51
|
def configure_ssl(http, ssl)
|
39
|
-
http
|
40
|
-
http
|
52
|
+
http_set(http, :verify_mode, ssl_verify_mode(ssl))
|
53
|
+
http_set(http, :cert_store, ssl_cert_store(ssl))
|
54
|
+
|
55
|
+
http_set(http, :certificate, ssl[:client_cert]) if ssl[:client_cert]
|
56
|
+
http_set(http, :private_key, ssl[:client_key]) if ssl[:client_key]
|
57
|
+
http_set(http, :ca_file, ssl[:ca_file]) if ssl[:ca_file]
|
58
|
+
http_set(http, :ssl_version, ssl[:version]) if ssl[:version]
|
59
|
+
end
|
41
60
|
|
42
|
-
|
43
|
-
http.
|
44
|
-
|
45
|
-
|
61
|
+
def http_set(http, attr, value)
|
62
|
+
if http.send(attr) != value
|
63
|
+
http.send("#{attr}=", value)
|
64
|
+
end
|
46
65
|
end
|
47
66
|
end
|
48
67
|
end
|
@@ -3,18 +3,14 @@ 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
|
-
|
11
6
|
def call(env)
|
12
7
|
super
|
13
|
-
|
14
8
|
# TODO: support streaming requests
|
15
9
|
env[:body] = env[:body].read if env[:body].respond_to? :read
|
16
10
|
|
17
|
-
session =
|
11
|
+
session = ::Patron::Session.new
|
12
|
+
@config_block.call(session) if @config_block
|
13
|
+
configure_ssl(session, env[:ssl]) if env[:url].scheme == 'https' and env[:ssl]
|
18
14
|
|
19
15
|
if req = env[:request]
|
20
16
|
session.timeout = session.connect_timeout = req[:timeout] if req[:timeout]
|
@@ -32,23 +28,26 @@ module Faraday
|
|
32
28
|
data = env[:body] ? env[:body].to_s : nil
|
33
29
|
session.request(env[:method], env[:url].to_s, env[:request_headers], :data => data)
|
34
30
|
rescue Errno::ECONNREFUSED, ::Patron::ConnectionFailed
|
35
|
-
raise
|
31
|
+
raise Faraday::ConnectionFailed, $!
|
36
32
|
end
|
37
33
|
|
38
|
-
|
34
|
+
# Remove the "HTTP/1.1 200", leaving just the reason phrase
|
35
|
+
reason_phrase = response.status_line.gsub(/^.* \d{3} /, '')
|
36
|
+
|
37
|
+
save_response(env, response.status, response.body, response.headers, reason_phrase)
|
39
38
|
|
40
39
|
@app.call env
|
41
40
|
rescue ::Patron::TimeoutError => err
|
42
|
-
if err.message
|
43
|
-
raise Faraday::
|
41
|
+
if connection_timed_out_message?(err.message)
|
42
|
+
raise Faraday::ConnectionFailed, err
|
44
43
|
else
|
45
|
-
raise Faraday::
|
44
|
+
raise Faraday::TimeoutError, err
|
46
45
|
end
|
47
46
|
rescue ::Patron::Error => err
|
48
47
|
if err.message.include?("code 407")
|
49
|
-
raise
|
48
|
+
raise Faraday::ConnectionFailed, %{407 "Proxy Authentication Required "}
|
50
49
|
else
|
51
|
-
raise
|
50
|
+
raise Faraday::ConnectionFailed, err
|
52
51
|
end
|
53
52
|
end
|
54
53
|
|
@@ -67,12 +66,30 @@ module Faraday
|
|
67
66
|
end
|
68
67
|
end
|
69
68
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
def configure_ssl(session, ssl)
|
70
|
+
if ssl.fetch(:verify, true)
|
71
|
+
session.cacert = ssl[:ca_file]
|
72
|
+
else
|
73
|
+
session.insecure = true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
CURL_TIMEOUT_MESSAGES = [ "Connection time-out",
|
80
|
+
"Connection timed out",
|
81
|
+
"Timed out before name resolve",
|
82
|
+
"server connect has timed out",
|
83
|
+
"Resolving timed out",
|
84
|
+
"name lookup timed out",
|
85
|
+
"timed out before SSL",
|
86
|
+
"connect() timed out"
|
87
|
+
].freeze
|
88
|
+
|
89
|
+
def connection_timed_out_message?(message)
|
90
|
+
CURL_TIMEOUT_MESSAGES.any? { |curl_message| message.include?(curl_message) }
|
75
91
|
end
|
92
|
+
|
76
93
|
end
|
77
94
|
end
|
78
95
|
end
|
data/lib/faraday/adapter/rack.rb
CHANGED
@@ -41,7 +41,7 @@ module Faraday
|
|
41
41
|
|
42
42
|
timeout = env[:request][:timeout] || env[:request][:open_timeout]
|
43
43
|
response = if timeout
|
44
|
-
Timer.timeout(timeout, Faraday::
|
44
|
+
Timer.timeout(timeout, Faraday::TimeoutError) { execute_request(env, rack_env) }
|
45
45
|
else
|
46
46
|
execute_request(env, rack_env)
|
47
47
|
end
|
data/lib/faraday/adapter/test.rb
CHANGED
@@ -1,16 +1,41 @@
|
|
1
1
|
module Faraday
|
2
2
|
class Adapter
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
3
|
+
# Examples
|
4
|
+
#
|
5
|
+
# test = Faraday::Connection.new do
|
6
|
+
# use Faraday::Adapter::Test do |stub|
|
7
|
+
# # simply define matcher to match the request
|
8
|
+
# stub.get '/resource.json' do
|
9
|
+
# # return static content
|
10
|
+
# [200, {'Content-Type' => 'application/json'}, 'hi world']
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# # response with content generated based on request
|
14
|
+
# stub.get '/showget' do |env|
|
15
|
+
# [200, {'Content-Type' => 'text/plain'}, env[:method].to_s]
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# # regular expression can be used as matching filter
|
19
|
+
# stub.get /\A\/items\/(\d+)\z/ do |env, meta|
|
20
|
+
# # in case regular expression is used an instance of MatchData can be received
|
21
|
+
# [200, {'Content-Type' => 'text/plain'}, "showing item: #{meta[:match_data][1]}"]
|
22
|
+
# end
|
7
23
|
# end
|
8
24
|
# end
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
25
|
+
#
|
26
|
+
# resp = test.get '/resource.json'
|
27
|
+
# resp.body # => 'hi world'
|
28
|
+
#
|
29
|
+
# resp = test.get '/showget'
|
30
|
+
# resp.body # => 'get'
|
31
|
+
#
|
32
|
+
# resp = test.get '/items/1'
|
33
|
+
# resp.body # => 'showing item: 1'
|
34
|
+
#
|
35
|
+
# resp = test.get '/items/2'
|
36
|
+
# resp.body # => 'showing item: 2'
|
13
37
|
#
|
38
|
+
|
14
39
|
class Test < Faraday::Adapter
|
15
40
|
attr_accessor :stubs
|
16
41
|
|
@@ -28,17 +53,17 @@ module Faraday
|
|
28
53
|
@stack.empty?
|
29
54
|
end
|
30
55
|
|
31
|
-
def match(request_method, path, headers, body)
|
56
|
+
def match(request_method, host, path, headers, body)
|
32
57
|
return false if !@stack.key?(request_method)
|
33
58
|
stack = @stack[request_method]
|
34
59
|
consumed = (@consumed[request_method] ||= [])
|
35
60
|
|
36
|
-
|
61
|
+
stub, meta = matches?(stack, host, path, headers, body)
|
62
|
+
if stub
|
37
63
|
consumed << stack.delete(stub)
|
38
|
-
stub
|
39
|
-
else
|
40
|
-
matches?(consumed, path, headers, body)
|
64
|
+
return stub, meta
|
41
65
|
end
|
66
|
+
matches?(consumed, host, path, headers, body)
|
42
67
|
end
|
43
68
|
|
44
69
|
def get(path, headers = {}, &block)
|
@@ -85,33 +110,55 @@ module Faraday
|
|
85
110
|
protected
|
86
111
|
|
87
112
|
def new_stub(request_method, path, headers = {}, body=nil, &block)
|
88
|
-
normalized_path =
|
89
|
-
|
113
|
+
normalized_path, host =
|
114
|
+
if path.is_a?(Regexp)
|
115
|
+
path
|
116
|
+
else
|
117
|
+
[Faraday::Utils.normalize_path(path), Faraday::Utils.URI(path).host]
|
118
|
+
end
|
119
|
+
|
120
|
+
(@stack[request_method] ||= []) << Stub.new(host, normalized_path, headers, body, block)
|
90
121
|
end
|
91
122
|
|
92
|
-
def matches?(stack, path, headers, body)
|
93
|
-
stack.
|
123
|
+
def matches?(stack, host, path, headers, body)
|
124
|
+
stack.each do |stub|
|
125
|
+
match_result, meta = stub.matches?(host, path, headers, body)
|
126
|
+
return stub, meta if match_result
|
127
|
+
end
|
128
|
+
nil
|
94
129
|
end
|
95
130
|
end
|
96
131
|
|
97
|
-
class Stub < Struct.new(:path, :params, :headers, :body, :block)
|
98
|
-
def initialize(full, headers, body, block)
|
99
|
-
path, query = full.split(
|
132
|
+
class Stub < Struct.new(:host, :path, :params, :headers, :body, :block)
|
133
|
+
def initialize(host, full, headers, body, block)
|
134
|
+
path, query = full.respond_to?(:split) ? full.split("?") : full
|
100
135
|
params = query ?
|
101
136
|
Faraday::Utils.parse_nested_query(query) :
|
102
137
|
{}
|
103
|
-
super(path, params, headers, body, block)
|
138
|
+
super(host, path, params, headers, body, block)
|
104
139
|
end
|
105
140
|
|
106
|
-
def matches?(request_uri, request_headers, request_body)
|
141
|
+
def matches?(request_host, request_uri, request_headers, request_body)
|
107
142
|
request_path, request_query = request_uri.split('?')
|
108
143
|
request_params = request_query ?
|
109
144
|
Faraday::Utils.parse_nested_query(request_query) :
|
110
145
|
{}
|
111
|
-
|
146
|
+
# meta is a hash use as carrier
|
147
|
+
# that will be yielded to consumer block
|
148
|
+
meta = {}
|
149
|
+
return (host.nil? || host == request_host) &&
|
150
|
+
path_match?(request_path, meta) &&
|
112
151
|
params_match?(request_params) &&
|
113
152
|
(body.to_s.size.zero? || request_body == body) &&
|
114
|
-
headers_match?(request_headers)
|
153
|
+
headers_match?(request_headers), meta
|
154
|
+
end
|
155
|
+
|
156
|
+
def path_match?(request_path, meta)
|
157
|
+
if path.is_a? Regexp
|
158
|
+
!!(meta[:match_data] = path.match(request_path))
|
159
|
+
else
|
160
|
+
path == request_path
|
161
|
+
end
|
115
162
|
end
|
116
163
|
|
117
164
|
def params_match?(request_params)
|
@@ -143,14 +190,18 @@ module Faraday
|
|
143
190
|
|
144
191
|
def call(env)
|
145
192
|
super
|
193
|
+
host = env[:url].host
|
146
194
|
normalized_path = Faraday::Utils.normalize_path(env[:url])
|
147
195
|
params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder
|
148
196
|
|
149
|
-
|
197
|
+
stub, meta = stubs.match(env[:method], host, normalized_path, env.request_headers, env[:body])
|
198
|
+
if stub
|
150
199
|
env[:params] = (query = env[:url].query) ?
|
151
|
-
params_encoder.decode(query)
|
152
|
-
|
153
|
-
status, headers, body =
|
200
|
+
params_encoder.decode(query) : {}
|
201
|
+
block_arity = stub.block.arity
|
202
|
+
status, headers, body = (block_arity >= 0) ?
|
203
|
+
stub.block.call(*[env, meta].take(block_arity)) :
|
204
|
+
stub.block.call(env, meta)
|
154
205
|
save_response(env, status, body, headers)
|
155
206
|
else
|
156
207
|
raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
|