faraday 0.17.6 → 1.10.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +156 -8
  3. data/LICENSE.md +1 -1
  4. data/README.md +16 -358
  5. data/Rakefile +1 -7
  6. data/examples/client_spec.rb +97 -0
  7. data/examples/client_test.rb +118 -0
  8. data/lib/faraday/adapter/test.rb +118 -69
  9. data/lib/faraday/adapter/typhoeus.rb +4 -1
  10. data/lib/faraday/adapter.rb +72 -22
  11. data/lib/faraday/adapter_registry.rb +30 -0
  12. data/lib/faraday/autoload.rb +39 -36
  13. data/lib/faraday/connection.rb +343 -185
  14. data/lib/faraday/dependency_loader.rb +39 -0
  15. data/lib/faraday/deprecate.rb +7 -6
  16. data/lib/faraday/encoders/flat_params_encoder.rb +105 -0
  17. data/lib/faraday/encoders/nested_params_encoder.rb +176 -0
  18. data/lib/faraday/error.rb +28 -40
  19. data/lib/faraday/logging/formatter.rb +105 -0
  20. data/lib/faraday/methods.rb +6 -0
  21. data/lib/faraday/middleware.rb +19 -25
  22. data/lib/faraday/middleware_registry.rb +129 -0
  23. data/lib/faraday/options/connection_options.rb +22 -0
  24. data/lib/faraday/options/env.rb +181 -0
  25. data/lib/faraday/options/proxy_options.rb +32 -0
  26. data/lib/faraday/options/request_options.rb +22 -0
  27. data/lib/faraday/options/ssl_options.rb +59 -0
  28. data/lib/faraday/options.rb +36 -191
  29. data/lib/faraday/parameters.rb +4 -197
  30. data/lib/faraday/rack_builder.rb +76 -64
  31. data/lib/faraday/request/authorization.rb +51 -30
  32. data/lib/faraday/request/basic_authentication.rb +14 -7
  33. data/lib/faraday/request/instrumentation.rb +45 -27
  34. data/lib/faraday/request/json.rb +55 -0
  35. data/lib/faraday/request/token_authentication.rb +15 -10
  36. data/lib/faraday/request/url_encoded.rb +43 -23
  37. data/lib/faraday/request.rb +82 -44
  38. data/lib/faraday/response/json.rb +54 -0
  39. data/lib/faraday/response/logger.rb +20 -69
  40. data/lib/faraday/response/raise_error.rb +49 -18
  41. data/lib/faraday/response.rb +26 -20
  42. data/lib/faraday/utils/headers.rb +139 -0
  43. data/lib/faraday/utils/params_hash.rb +61 -0
  44. data/lib/faraday/utils.rb +38 -247
  45. data/lib/faraday/version.rb +5 -0
  46. data/lib/faraday.rb +134 -188
  47. data/spec/external_adapters/faraday_specs_setup.rb +14 -0
  48. data/spec/faraday/adapter/em_http_spec.rb +49 -0
  49. data/spec/faraday/adapter/em_synchrony_spec.rb +18 -0
  50. data/spec/faraday/adapter/excon_spec.rb +49 -0
  51. data/spec/faraday/adapter/httpclient_spec.rb +73 -0
  52. data/spec/faraday/adapter/net_http_spec.rb +64 -0
  53. data/spec/faraday/adapter/patron_spec.rb +18 -0
  54. data/spec/faraday/adapter/rack_spec.rb +8 -0
  55. data/spec/faraday/adapter/test_spec.rb +377 -0
  56. data/spec/faraday/adapter/typhoeus_spec.rb +7 -0
  57. data/spec/faraday/adapter_registry_spec.rb +28 -0
  58. data/spec/faraday/adapter_spec.rb +55 -0
  59. data/spec/faraday/composite_read_io_spec.rb +80 -0
  60. data/spec/faraday/connection_spec.rb +736 -0
  61. data/spec/faraday/deprecate_spec.rb +17 -17
  62. data/spec/faraday/error_spec.rb +12 -54
  63. data/spec/faraday/middleware_spec.rb +52 -0
  64. data/spec/faraday/options/env_spec.rb +70 -0
  65. data/spec/faraday/options/options_spec.rb +297 -0
  66. data/spec/faraday/options/proxy_options_spec.rb +44 -0
  67. data/spec/faraday/options/request_options_spec.rb +19 -0
  68. data/spec/faraday/params_encoders/flat_spec.rb +42 -0
  69. data/spec/faraday/params_encoders/nested_spec.rb +142 -0
  70. data/spec/faraday/rack_builder_spec.rb +345 -0
  71. data/spec/faraday/request/authorization_spec.rb +96 -0
  72. data/spec/faraday/request/instrumentation_spec.rb +76 -0
  73. data/spec/faraday/request/json_spec.rb +111 -0
  74. data/spec/faraday/request/url_encoded_spec.rb +83 -0
  75. data/spec/faraday/request_spec.rb +120 -0
  76. data/spec/faraday/response/json_spec.rb +119 -0
  77. data/spec/faraday/response/logger_spec.rb +220 -0
  78. data/spec/faraday/response/middleware_spec.rb +68 -0
  79. data/spec/faraday/response/raise_error_spec.rb +78 -15
  80. data/spec/faraday/response_spec.rb +75 -0
  81. data/spec/faraday/utils/headers_spec.rb +82 -0
  82. data/spec/faraday/utils_spec.rb +56 -0
  83. data/spec/faraday_spec.rb +37 -0
  84. data/spec/spec_helper.rb +65 -36
  85. data/spec/support/disabling_stub.rb +14 -0
  86. data/spec/support/fake_safe_buffer.rb +15 -0
  87. data/spec/support/helper_methods.rb +133 -0
  88. data/spec/support/shared_examples/adapter.rb +105 -0
  89. data/spec/support/shared_examples/params_encoder.rb +18 -0
  90. data/spec/support/shared_examples/request_method.rb +262 -0
  91. data/spec/support/streaming_response_checker.rb +35 -0
  92. data/spec/support/webmock_rack_app.rb +68 -0
  93. metadata +210 -56
  94. data/lib/faraday/adapter/em_http.rb +0 -243
  95. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -56
  96. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -66
  97. data/lib/faraday/adapter/em_synchrony.rb +0 -106
  98. data/lib/faraday/adapter/excon.rb +0 -82
  99. data/lib/faraday/adapter/httpclient.rb +0 -128
  100. data/lib/faraday/adapter/net_http.rb +0 -153
  101. data/lib/faraday/adapter/net_http_persistent.rb +0 -68
  102. data/lib/faraday/adapter/patron.rb +0 -95
  103. data/lib/faraday/adapter/rack.rb +0 -58
  104. data/lib/faraday/request/multipart.rb +0 -68
  105. data/lib/faraday/request/retry.rb +0 -213
  106. data/lib/faraday/upload_io.rb +0 -77
  107. data/test/adapters/default_test.rb +0 -14
  108. data/test/adapters/em_http_test.rb +0 -30
  109. data/test/adapters/em_synchrony_test.rb +0 -32
  110. data/test/adapters/excon_test.rb +0 -30
  111. data/test/adapters/httpclient_test.rb +0 -34
  112. data/test/adapters/integration.rb +0 -263
  113. data/test/adapters/logger_test.rb +0 -136
  114. data/test/adapters/net_http_persistent_test.rb +0 -114
  115. data/test/adapters/net_http_test.rb +0 -79
  116. data/test/adapters/patron_test.rb +0 -40
  117. data/test/adapters/rack_test.rb +0 -38
  118. data/test/adapters/test_middleware_test.rb +0 -157
  119. data/test/adapters/typhoeus_test.rb +0 -38
  120. data/test/authentication_middleware_test.rb +0 -65
  121. data/test/composite_read_io_test.rb +0 -109
  122. data/test/connection_test.rb +0 -738
  123. data/test/env_test.rb +0 -268
  124. data/test/helper.rb +0 -75
  125. data/test/live_server.rb +0 -67
  126. data/test/middleware/instrumentation_test.rb +0 -88
  127. data/test/middleware/retry_test.rb +0 -282
  128. data/test/middleware_stack_test.rb +0 -260
  129. data/test/multibyte.txt +0 -1
  130. data/test/options_test.rb +0 -333
  131. data/test/parameters_test.rb +0 -157
  132. data/test/request_middleware_test.rb +0 -126
  133. data/test/response_middleware_test.rb +0 -72
  134. data/test/strawberry.rb +0 -2
  135. data/test/utils_test.rb +0 -98
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requires Ruby with rspec and faraday gems.
4
+ # rspec client_spec.rb
5
+
6
+ require 'faraday'
7
+ require 'json'
8
+
9
+ # Example API client
10
+ class Client
11
+ def initialize(conn)
12
+ @conn = conn
13
+ end
14
+
15
+ def sushi(jname, params: {})
16
+ res = @conn.get("/#{jname}", params)
17
+ data = JSON.parse(res.body)
18
+ data['name']
19
+ end
20
+ end
21
+
22
+ RSpec.describe Client do
23
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new }
24
+ let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } }
25
+ let(:client) { Client.new(conn) }
26
+
27
+ it 'parses name' do
28
+ stubs.get('/ebi') do |env|
29
+ # optional: you can inspect the Faraday::Env
30
+ expect(env.url.path).to eq('/ebi')
31
+ [
32
+ 200,
33
+ { 'Content-Type': 'application/javascript' },
34
+ '{"name": "shrimp"}'
35
+ ]
36
+ end
37
+
38
+ # uncomment to trigger stubs.verify_stubbed_calls failure
39
+ # stubs.get('/unused') { [404, {}, ''] }
40
+
41
+ expect(client.sushi('ebi')).to eq('shrimp')
42
+ stubs.verify_stubbed_calls
43
+ end
44
+
45
+ it 'handles 404' do
46
+ stubs.get('/ebi') do
47
+ [
48
+ 404,
49
+ { 'Content-Type': 'application/javascript' },
50
+ '{}'
51
+ ]
52
+ end
53
+ expect(client.sushi('ebi')).to be_nil
54
+ stubs.verify_stubbed_calls
55
+ end
56
+
57
+ it 'handles exception' do
58
+ stubs.get('/ebi') do
59
+ raise Faraday::ConnectionFailed, nil
60
+ end
61
+
62
+ expect { client.sushi('ebi') }.to raise_error(Faraday::ConnectionFailed)
63
+ stubs.verify_stubbed_calls
64
+ end
65
+
66
+ context 'When the test stub is run in strict_mode' do
67
+ let(:stubs) { Faraday::Adapter::Test::Stubs.new(strict_mode: true) }
68
+
69
+ it 'verifies the all parameter values are identical' do
70
+ stubs.get('/ebi?abc=123') do
71
+ [
72
+ 200,
73
+ { 'Content-Type': 'application/javascript' },
74
+ '{"name": "shrimp"}'
75
+ ]
76
+ end
77
+
78
+ # uncomment to raise Stubs::NotFound
79
+ # expect(client.sushi('ebi', params: { abc: 123, foo: 'Kappa' })).to eq('shrimp')
80
+ expect(client.sushi('ebi', params: { abc: 123 })).to eq('shrimp')
81
+ stubs.verify_stubbed_calls
82
+ end
83
+ end
84
+
85
+ context 'When the Faraday connection is configured with FlatParamsEncoder' do
86
+ let(:conn) { Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) { |b| b.adapter(:test, stubs) } }
87
+
88
+ it 'handles the same multiple URL parameters' do
89
+ stubs.get('/ebi?a=x&a=y&a=z') { [200, { 'Content-Type' => 'application/json' }, '{"name": "shrimp"}'] }
90
+
91
+ # uncomment to raise Stubs::NotFound
92
+ # expect(client.sushi('ebi', params: { a: %w[x y] })).to eq('shrimp')
93
+ expect(client.sushi('ebi', params: { a: %w[x y z] })).to eq('shrimp')
94
+ stubs.verify_stubbed_calls
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requires Ruby with test-unit and faraday gems.
4
+ # ruby client_test.rb
5
+
6
+ require 'faraday'
7
+ require 'json'
8
+ require 'test/unit'
9
+
10
+ # Example API client
11
+ class Client
12
+ def initialize(conn)
13
+ @conn = conn
14
+ end
15
+
16
+ def sushi(jname, params: {})
17
+ res = @conn.get("/#{jname}", params)
18
+ data = JSON.parse(res.body)
19
+ data['name']
20
+ end
21
+ end
22
+
23
+ # Example API client test
24
+ class ClientTest < Test::Unit::TestCase
25
+ def test_sushi_name
26
+ stubs = Faraday::Adapter::Test::Stubs.new
27
+ stubs.get('/ebi') do |env|
28
+ # optional: you can inspect the Faraday::Env
29
+ assert_equal '/ebi', env.url.path
30
+ [
31
+ 200,
32
+ { 'Content-Type': 'application/javascript' },
33
+ '{"name": "shrimp"}'
34
+ ]
35
+ end
36
+
37
+ # uncomment to trigger stubs.verify_stubbed_calls failure
38
+ # stubs.get('/unused') { [404, {}, ''] }
39
+
40
+ cli = client(stubs)
41
+ assert_equal 'shrimp', cli.sushi('ebi')
42
+ stubs.verify_stubbed_calls
43
+ end
44
+
45
+ def test_sushi_404
46
+ stubs = Faraday::Adapter::Test::Stubs.new
47
+ stubs.get('/ebi') do
48
+ [
49
+ 404,
50
+ { 'Content-Type': 'application/javascript' },
51
+ '{}'
52
+ ]
53
+ end
54
+
55
+ cli = client(stubs)
56
+ assert_nil cli.sushi('ebi')
57
+ stubs.verify_stubbed_calls
58
+ end
59
+
60
+ def test_sushi_exception
61
+ stubs = Faraday::Adapter::Test::Stubs.new
62
+ stubs.get('/ebi') do
63
+ raise Faraday::ConnectionFailed, nil
64
+ end
65
+
66
+ cli = client(stubs)
67
+ assert_raise Faraday::ConnectionFailed do
68
+ cli.sushi('ebi')
69
+ end
70
+ stubs.verify_stubbed_calls
71
+ end
72
+
73
+ def test_strict_mode
74
+ stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
75
+ stubs.get('/ebi?abc=123') do
76
+ [
77
+ 200,
78
+ { 'Content-Type': 'application/javascript' },
79
+ '{"name": "shrimp"}'
80
+ ]
81
+ end
82
+
83
+ cli = client(stubs)
84
+ assert_equal 'shrimp', cli.sushi('ebi', params: { abc: 123 })
85
+
86
+ # uncomment to raise Stubs::NotFound
87
+ # assert_equal 'shrimp', cli.sushi('ebi', params: { abc: 123, foo: 'Kappa' })
88
+ stubs.verify_stubbed_calls
89
+ end
90
+
91
+ def test_non_default_params_encoder
92
+ stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
93
+ stubs.get('/ebi?a=x&a=y&a=z') do
94
+ [
95
+ 200,
96
+ { 'Content-Type': 'application/javascript' },
97
+ '{"name": "shrimp"}'
98
+ ]
99
+ end
100
+ conn = Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder|
101
+ builder.adapter :test, stubs
102
+ end
103
+
104
+ cli = Client.new(conn)
105
+ assert_equal 'shrimp', cli.sushi('ebi', params: { a: %w[x y z] })
106
+
107
+ # uncomment to raise Stubs::NotFound
108
+ # assert_equal 'shrimp', cli.sushi('ebi', params: { a: %w[x y] })
109
+ stubs.verify_stubbed_calls
110
+ end
111
+
112
+ def client(stubs)
113
+ conn = Faraday.new do |builder|
114
+ builder.adapter :test, stubs
115
+ end
116
+ Client.new(conn)
117
+ end
118
+ end
@@ -1,51 +1,60 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
3
- # Examples
4
- #
5
+ # @example
5
6
  # test = Faraday::Connection.new do
6
7
  # use Faraday::Adapter::Test do |stub|
7
- # # simply define matcher to match the request
8
+ # # Define matcher to match the request
8
9
  # stub.get '/resource.json' do
9
10
  # # return static content
10
11
  # [200, {'Content-Type' => 'application/json'}, 'hi world']
11
12
  # end
12
- #
13
+ #
13
14
  # # response with content generated based on request
14
15
  # stub.get '/showget' do |env|
15
16
  # [200, {'Content-Type' => 'text/plain'}, env[:method].to_s]
16
17
  # end
17
- #
18
- # # regular expression can be used as matching filter
18
+ #
19
+ # # A regular expression can be used as matching filter
19
20
  # 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]}"]
21
+ # # in case regular expression is used, an instance of MatchData
22
+ # # can be received
23
+ # [200,
24
+ # {'Content-Type' => 'text/plain'},
25
+ # "showing item: #{meta[:match_data][1]}"
26
+ # ]
22
27
  # end
28
+ #
29
+ # # You can set strict_mode to exactly match the stubbed requests.
30
+ # stub.strict_mode = true
23
31
  # end
24
32
  # end
25
- #
33
+ #
26
34
  # resp = test.get '/resource.json'
27
35
  # resp.body # => 'hi world'
28
- #
36
+ #
29
37
  # resp = test.get '/showget'
30
38
  # resp.body # => 'get'
31
- #
39
+ #
32
40
  # resp = test.get '/items/1'
33
41
  # resp.body # => 'showing item: 1'
34
- #
42
+ #
35
43
  # resp = test.get '/items/2'
36
44
  # resp.body # => 'showing item: 2'
37
- #
38
-
39
45
  class Test < Faraday::Adapter
40
46
  attr_accessor :stubs
41
47
 
48
+ # A stack of Stubs
42
49
  class Stubs
43
50
  class NotFound < StandardError
44
51
  end
45
52
 
46
- def initialize
47
- # {:get => [Stub, Stub]}
48
- @stack, @consumed = {}, {}
53
+ def initialize(strict_mode: false)
54
+ # { get: [Stub, Stub] }
55
+ @stack = {}
56
+ @consumed = {}
57
+ @strict_mode = strict_mode
49
58
  yield(self) if block_given?
50
59
  end
51
60
 
@@ -53,17 +62,20 @@ module Faraday
53
62
  @stack.empty?
54
63
  end
55
64
 
56
- def match(request_method, host, path, headers, body)
57
- return false if !@stack.key?(request_method)
65
+ # @param env [Faraday::Env]
66
+ def match(env)
67
+ request_method = env[:method]
68
+ return false unless @stack.key?(request_method)
69
+
58
70
  stack = @stack[request_method]
59
71
  consumed = (@consumed[request_method] ||= [])
60
72
 
61
- stub, meta = matches?(stack, host, path, headers, body)
73
+ stub, meta = matches?(stack, env)
62
74
  if stub
63
75
  consumed << stack.delete(stub)
64
76
  return stub, meta
65
77
  end
66
- matches?(consumed, host, path, headers, body)
78
+ matches?(consumed, env)
67
79
  end
68
80
 
69
81
  def get(path, headers = {}, &block)
@@ -74,15 +86,15 @@ module Faraday
74
86
  new_stub(:head, path, headers, &block)
75
87
  end
76
88
 
77
- def post(path, body=nil, headers = {}, &block)
89
+ def post(path, body = nil, headers = {}, &block)
78
90
  new_stub(:post, path, headers, body, &block)
79
91
  end
80
92
 
81
- def put(path, body=nil, headers = {}, &block)
93
+ def put(path, body = nil, headers = {}, &block)
82
94
  new_stub(:put, path, headers, body, &block)
83
95
  end
84
96
 
85
- def patch(path, body=nil, headers = {}, &block)
97
+ def patch(path, body = nil, headers = {}, &block)
86
98
  new_stub(:patch, path, headers, body, &block)
87
99
  end
88
100
 
@@ -98,76 +110,109 @@ module Faraday
98
110
  def verify_stubbed_calls
99
111
  failed_stubs = []
100
112
  @stack.each do |method, stubs|
101
- unless stubs.size == 0
102
- failed_stubs.concat(stubs.map {|stub|
113
+ next if stubs.empty?
114
+
115
+ failed_stubs.concat(
116
+ stubs.map do |stub|
103
117
  "Expected #{method} #{stub}."
104
- })
118
+ end
119
+ )
120
+ end
121
+ raise failed_stubs.join(' ') unless failed_stubs.empty?
122
+ end
123
+
124
+ # Set strict_mode. If the value is true, this adapter tries to find matched requests strictly,
125
+ # which means that all of a path, parameters, and headers must be the same as an actual request.
126
+ def strict_mode=(value)
127
+ @strict_mode = value
128
+ @stack.each do |_method, stubs|
129
+ stubs.each do |stub|
130
+ stub.strict_mode = value
105
131
  end
106
132
  end
107
- raise failed_stubs.join(" ") unless failed_stubs.size == 0
108
133
  end
109
134
 
110
135
  protected
111
136
 
112
- def new_stub(request_method, path, headers = {}, body=nil, &block)
137
+ def new_stub(request_method, path, headers = {}, body = nil, &block)
113
138
  normalized_path, host =
114
139
  if path.is_a?(Regexp)
115
140
  path
116
141
  else
117
- [Faraday::Utils.normalize_path(path), Faraday::Utils.URI(path).host]
142
+ [
143
+ Faraday::Utils.normalize_path(path),
144
+ Faraday::Utils.URI(path).host
145
+ ]
118
146
  end
147
+ path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path
148
+ headers = Utils::Headers.new(headers)
119
149
 
120
- (@stack[request_method] ||= []) << Stub.new(host, normalized_path, headers, body, block)
150
+ stub = Stub.new(host, path, query, headers, body, @strict_mode, block)
151
+ (@stack[request_method] ||= []) << stub
121
152
  end
122
153
 
123
- def matches?(stack, host, path, headers, body)
154
+ # @param stack [Hash]
155
+ # @param env [Faraday::Env]
156
+ def matches?(stack, env)
124
157
  stack.each do |stub|
125
- match_result, meta = stub.matches?(host, path, headers, body)
158
+ match_result, meta = stub.matches?(env)
126
159
  return stub, meta if match_result
127
160
  end
128
161
  nil
129
162
  end
130
163
  end
131
164
 
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
135
- params = query ?
136
- Faraday::Utils.parse_nested_query(query) :
137
- {}
138
- super(host, path, params, headers, body, block)
139
- end
140
-
141
- def matches?(request_host, request_uri, request_headers, request_body)
142
- request_path, request_query = request_uri.split('?')
143
- request_params = request_query ?
144
- Faraday::Utils.parse_nested_query(request_query) :
145
- {}
146
- # meta is a hash use as carrier
165
+ # Stub request
166
+ class Stub < Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) # rubocop:disable Style/StructInheritance
167
+ # @param env [Faraday::Env]
168
+ def matches?(env)
169
+ request_host = env[:url].host
170
+ request_path = Faraday::Utils.normalize_path(env[:url].path)
171
+ request_headers = env.request_headers
172
+ request_body = env[:body]
173
+
174
+ # meta is a hash used as carrier
147
175
  # that will be yielded to consumer block
148
176
  meta = {}
149
- return (host.nil? || host == request_host) &&
177
+ [(host.nil? || host == request_host) &&
150
178
  path_match?(request_path, meta) &&
151
- params_match?(request_params) &&
179
+ params_match?(env) &&
152
180
  (body.to_s.size.zero? || request_body == body) &&
153
- headers_match?(request_headers), meta
181
+ headers_match?(request_headers), meta]
154
182
  end
155
183
 
156
184
  def path_match?(request_path, meta)
157
- if path.is_a? Regexp
185
+ if path.is_a?(Regexp)
158
186
  !!(meta[:match_data] = path.match(request_path))
159
187
  else
160
188
  path == request_path
161
189
  end
162
190
  end
163
191
 
164
- def params_match?(request_params)
192
+ # @param env [Faraday::Env]
193
+ def params_match?(env)
194
+ request_params = env[:params]
195
+ params = env.params_encoder.decode(query) || {}
196
+
197
+ if strict_mode
198
+ return Set.new(params) == Set.new(request_params)
199
+ end
200
+
165
201
  params.keys.all? do |key|
166
202
  request_params[key] == params[key]
167
203
  end
168
204
  end
169
205
 
170
206
  def headers_match?(request_headers)
207
+ if strict_mode
208
+ headers_with_user_agent = headers.dup.tap do |hs|
209
+ # NOTE: Set User-Agent in case it's not set when creating Stubs.
210
+ # Users would not want to set Faraday's User-Agent explicitly.
211
+ hs[:user_agent] ||= Connection::USER_AGENT
212
+ end
213
+ return Set.new(headers_with_user_agent) == Set.new(request_headers)
214
+ end
215
+
171
216
  headers.keys.all? do |key|
172
217
  request_headers[key] == headers[key]
173
218
  end
@@ -178,7 +223,7 @@ module Faraday
178
223
  end
179
224
  end
180
225
 
181
- def initialize(app, stubs=nil, &block)
226
+ def initialize(app, stubs = nil, &block)
182
227
  super(app)
183
228
  @stubs = stubs || Stubs.new
184
229
  configure(&block) if block
@@ -188,24 +233,28 @@ module Faraday
188
233
  yield(stubs)
189
234
  end
190
235
 
236
+ # @param env [Faraday::Env]
191
237
  def call(env)
192
238
  super
193
- host = env[:url].host
194
- normalized_path = Faraday::Utils.normalize_path(env[:url])
195
- params_encoder = env.request.params_encoder || Faraday::Utils.default_params_encoder
196
-
197
- stub, meta = stubs.match(env[:method], host, normalized_path, env.request_headers, env[:body])
198
- if stub
199
- env[:params] = (query = env[:url].query) ?
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)
205
- save_response(env, status, body, headers)
206
- else
207
- raise Stubs::NotFound, "no stubbed request for #{env[:method]} #{normalized_path} #{env[:body]}"
239
+
240
+ env.request.params_encoder ||= Faraday::Utils.default_params_encoder
241
+ env[:params] = env.params_encoder.decode(env[:url].query) || {}
242
+ stub, meta = stubs.match(env)
243
+
244
+ unless stub
245
+ raise Stubs::NotFound, "no stubbed request for #{env[:method]} "\
246
+ "#{env[:url]} #{env[:body]}"
208
247
  end
248
+
249
+ block_arity = stub.block.arity
250
+ status, headers, body =
251
+ if block_arity >= 0
252
+ stub.block.call(*[env, meta].take(block_arity))
253
+ else
254
+ stub.block.call(env, meta)
255
+ end
256
+ save_response(env, status, body, headers)
257
+
209
258
  @app.call(env)
210
259
  end
211
260
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
4
  class Adapter
3
- # This class is just a stub, the real adapter is in https://github.com/philsturgeon/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
5
+ # Typhoeus adapter. This class is just a stub, the real adapter is in
6
+ # https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
4
7
  class Typhoeus < Faraday::Adapter
5
8
  # Needs to define this method in order to support Typhoeus <= 1.3.0
6
9
  def call; end
@@ -1,43 +1,65 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Faraday
2
- # Public: This is a base class for all Faraday adapters. Adapters are
4
+ # Base class for all Faraday adapters. Adapters are
3
5
  # responsible for fulfilling a Faraday request.
4
- class Adapter < Middleware
5
- CONTENT_LENGTH = 'Content-Length'.freeze
6
-
7
- register_middleware File.expand_path('../adapter', __FILE__),
8
- :test => [:Test, 'test'],
9
- :net_http => [:NetHttp, 'net_http'],
10
- :net_http_persistent => [:NetHttpPersistent, 'net_http_persistent'],
11
- :typhoeus => [:Typhoeus, 'typhoeus'],
12
- :patron => [:Patron, 'patron'],
13
- :em_synchrony => [:EMSynchrony, 'em_synchrony'],
14
- :em_http => [:EMHttp, 'em_http'],
15
- :excon => [:Excon, 'excon'],
16
- :rack => [:Rack, 'rack'],
17
- :httpclient => [:HTTPClient, 'httpclient']
18
-
19
- # Public: This module marks an Adapter as supporting parallel requests.
6
+ class Adapter
7
+ extend MiddlewareRegistry
8
+ extend DependencyLoader
9
+
10
+ CONTENT_LENGTH = 'Content-Length'
11
+
12
+ register_middleware File.expand_path('adapter', __dir__),
13
+ test: [:Test, 'test'],
14
+ typhoeus: [:Typhoeus, 'typhoeus']
15
+
16
+ # This module marks an Adapter as supporting parallel requests.
20
17
  module Parallelism
21
18
  attr_writer :supports_parallel
22
- def supports_parallel?() @supports_parallel end
19
+
20
+ def supports_parallel?
21
+ @supports_parallel
22
+ end
23
23
 
24
24
  def inherited(subclass)
25
25
  super
26
- subclass.supports_parallel = self.supports_parallel?
26
+ subclass.supports_parallel = supports_parallel?
27
27
  end
28
28
  end
29
29
 
30
30
  extend Parallelism
31
31
  self.supports_parallel = false
32
32
 
33
- def initialize(app = nil, opts = {}, &block)
34
- super(app)
33
+ def initialize(_app = nil, opts = {}, &block)
34
+ @app = ->(env) { env.response }
35
35
  @connection_options = opts
36
36
  @config_block = block
37
37
  end
38
38
 
39
+ # Yields or returns an adapter's configured connection. Depends on
40
+ # #build_connection being defined on this adapter.
41
+ #
42
+ # @param env [Faraday::Env, Hash] The env object for a faraday request.
43
+ #
44
+ # @return The return value of the given block, or the HTTP connection object
45
+ # if no block is given.
46
+ def connection(env)
47
+ conn = build_connection(env)
48
+ return conn unless block_given?
49
+
50
+ yield conn
51
+ end
52
+
53
+ # Close any persistent connections. The adapter should still be usable
54
+ # after calling close.
55
+ def close
56
+ # Possible implementation:
57
+ # @app.close if @app.respond_to?(:close)
58
+ end
59
+
39
60
  def call(env)
40
61
  env.clear_body if env.needs_body?
62
+ env.response = Response.new
41
63
  end
42
64
 
43
65
  private
@@ -45,11 +67,39 @@ module Faraday
45
67
  def save_response(env, status, body, headers = nil, reason_phrase = nil)
46
68
  env.status = status
47
69
  env.body = body
48
- env.reason_phrase = reason_phrase && reason_phrase.to_s.strip
70
+ env.reason_phrase = reason_phrase&.to_s&.strip
49
71
  env.response_headers = Utils::Headers.new.tap do |response_headers|
50
72
  response_headers.update headers unless headers.nil?
51
73
  yield(response_headers) if block_given?
52
74
  end
75
+
76
+ env.response.finish(env) unless env.parallel?
77
+ env.response
78
+ end
79
+
80
+ # Fetches either a read, write, or open timeout setting. Defaults to the
81
+ # :timeout value if a more specific one is not given.
82
+ #
83
+ # @param type [Symbol] Describes which timeout setting to get: :read,
84
+ # :write, or :open.
85
+ # @param options [Hash] Hash containing Symbol keys like :timeout,
86
+ # :read_timeout, :write_timeout, :open_timeout, or
87
+ # :timeout
88
+ #
89
+ # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
90
+ # has been set.
91
+ def request_timeout(type, options)
92
+ key = TIMEOUT_KEYS.fetch(type) do
93
+ msg = "Expected :read, :write, :open. Got #{type.inspect} :("
94
+ raise ArgumentError, msg
95
+ end
96
+ options[key] || options[:timeout]
53
97
  end
98
+
99
+ TIMEOUT_KEYS = {
100
+ read: :read_timeout,
101
+ open: :open_timeout,
102
+ write: :write_timeout
103
+ }.freeze
54
104
  end
55
105
  end