faraday 1.0.0 → 2.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.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -1
  3. data/LICENSE.md +1 -1
  4. data/README.md +35 -23
  5. data/Rakefile +3 -1
  6. data/examples/client_spec.rb +68 -14
  7. data/examples/client_test.rb +80 -15
  8. data/lib/faraday/adapter/test.rb +117 -52
  9. data/lib/faraday/adapter.rb +6 -20
  10. data/lib/faraday/adapter_registry.rb +3 -1
  11. data/lib/faraday/connection.rb +73 -132
  12. data/lib/faraday/encoders/flat_params_encoder.rb +9 -2
  13. data/lib/faraday/encoders/nested_params_encoder.rb +20 -8
  14. data/lib/faraday/error.rb +37 -8
  15. data/lib/faraday/logging/formatter.rb +28 -15
  16. data/lib/faraday/methods.rb +6 -0
  17. data/lib/faraday/middleware.rb +17 -5
  18. data/lib/faraday/middleware_registry.rb +17 -63
  19. data/lib/faraday/options/connection_options.rb +7 -6
  20. data/lib/faraday/options/env.rb +85 -62
  21. data/lib/faraday/options/proxy_options.rb +11 -3
  22. data/lib/faraday/options/request_options.rb +7 -6
  23. data/lib/faraday/options/ssl_options.rb +56 -45
  24. data/lib/faraday/options.rb +11 -14
  25. data/lib/faraday/rack_builder.rb +35 -32
  26. data/lib/faraday/request/authorization.rb +37 -36
  27. data/lib/faraday/request/instrumentation.rb +5 -1
  28. data/lib/faraday/request/json.rb +70 -0
  29. data/lib/faraday/request/url_encoded.rb +8 -2
  30. data/lib/faraday/request.rb +22 -29
  31. data/lib/faraday/response/json.rb +73 -0
  32. data/lib/faraday/response/logger.rb +8 -4
  33. data/lib/faraday/response/raise_error.rb +41 -3
  34. data/lib/faraday/response.rb +10 -23
  35. data/lib/faraday/utils/headers.rb +9 -4
  36. data/lib/faraday/utils.rb +22 -10
  37. data/lib/faraday/version.rb +5 -0
  38. data/lib/faraday.rb +49 -58
  39. data/spec/faraday/adapter/test_spec.rb +442 -0
  40. data/spec/faraday/connection_spec.rb +207 -90
  41. data/spec/faraday/error_spec.rb +45 -5
  42. data/spec/faraday/middleware_registry_spec.rb +31 -0
  43. data/spec/faraday/middleware_spec.rb +50 -6
  44. data/spec/faraday/options/env_spec.rb +8 -2
  45. data/spec/faraday/options/options_spec.rb +1 -1
  46. data/spec/faraday/options/proxy_options_spec.rb +15 -0
  47. data/spec/faraday/params_encoders/flat_spec.rb +8 -0
  48. data/spec/faraday/params_encoders/nested_spec.rb +16 -0
  49. data/spec/faraday/rack_builder_spec.rb +171 -50
  50. data/spec/faraday/request/authorization_spec.rb +54 -24
  51. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  52. data/spec/faraday/request/json_spec.rb +199 -0
  53. data/spec/faraday/request/url_encoded_spec.rb +25 -2
  54. data/spec/faraday/request_spec.rb +11 -10
  55. data/spec/faraday/response/json_spec.rb +189 -0
  56. data/spec/faraday/response/logger_spec.rb +38 -0
  57. data/spec/faraday/response/raise_error_spec.rb +105 -0
  58. data/spec/faraday/response_spec.rb +3 -1
  59. data/spec/faraday/utils/headers_spec.rb +22 -4
  60. data/spec/faraday/utils_spec.rb +63 -1
  61. data/spec/faraday_spec.rb +8 -4
  62. data/spec/spec_helper.rb +6 -5
  63. data/spec/support/fake_safe_buffer.rb +1 -1
  64. data/spec/support/helper_methods.rb +0 -37
  65. data/spec/support/shared_examples/adapter.rb +4 -3
  66. data/spec/support/shared_examples/request_method.rb +60 -31
  67. metadata +19 -44
  68. data/UPGRADING.md +0 -55
  69. data/lib/faraday/adapter/em_http.rb +0 -285
  70. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
  71. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
  72. data/lib/faraday/adapter/em_synchrony.rb +0 -150
  73. data/lib/faraday/adapter/excon.rb +0 -124
  74. data/lib/faraday/adapter/httpclient.rb +0 -151
  75. data/lib/faraday/adapter/net_http.rb +0 -209
  76. data/lib/faraday/adapter/net_http_persistent.rb +0 -91
  77. data/lib/faraday/adapter/patron.rb +0 -132
  78. data/lib/faraday/adapter/rack.rb +0 -75
  79. data/lib/faraday/adapter/typhoeus.rb +0 -15
  80. data/lib/faraday/autoload.rb +0 -95
  81. data/lib/faraday/dependency_loader.rb +0 -37
  82. data/lib/faraday/file_part.rb +0 -128
  83. data/lib/faraday/param_part.rb +0 -53
  84. data/lib/faraday/request/basic_authentication.rb +0 -20
  85. data/lib/faraday/request/multipart.rb +0 -99
  86. data/lib/faraday/request/retry.rb +0 -239
  87. data/lib/faraday/request/token_authentication.rb +0 -20
  88. data/spec/faraday/adapter/em_http_spec.rb +0 -47
  89. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -16
  90. data/spec/faraday/adapter/excon_spec.rb +0 -49
  91. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  92. data/spec/faraday/adapter/net_http_persistent_spec.rb +0 -57
  93. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  94. data/spec/faraday/adapter/patron_spec.rb +0 -18
  95. data/spec/faraday/adapter/rack_spec.rb +0 -8
  96. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  97. data/spec/faraday/composite_read_io_spec.rb +0 -80
  98. data/spec/faraday/request/multipart_spec.rb +0 -274
  99. data/spec/faraday/request/retry_spec.rb +0 -242
  100. data/spec/faraday/response/middleware_spec.rb +0 -52
  101. data/spec/support/webmock_rack_app.rb +0 -68
@@ -13,24 +13,29 @@ class Client
13
13
  @conn = conn
14
14
  end
15
15
 
16
- def sushi(jname)
17
- res = @conn.get("/#{jname}")
16
+ def httpbingo(jname, params: {})
17
+ res = @conn.get("/#{jname}", params)
18
18
  data = JSON.parse(res.body)
19
- data['name']
19
+ data['origin']
20
+ end
21
+
22
+ def foo(params)
23
+ res = @conn.post('/foo', JSON.dump(params))
24
+ res.status
20
25
  end
21
26
  end
22
27
 
23
28
  # Example API client test
24
29
  class ClientTest < Test::Unit::TestCase
25
- def test_sushi_name
30
+ def test_httpbingo_name
26
31
  stubs = Faraday::Adapter::Test::Stubs.new
27
- stubs.get('/ebi') do |env|
32
+ stubs.get('/api') do |env|
28
33
  # optional: you can inspect the Faraday::Env
29
- assert_equal '/ebi', env.url.path
34
+ assert_equal '/api', env.url.path
30
35
  [
31
36
  200,
32
37
  { 'Content-Type': 'application/javascript' },
33
- '{"name": "shrimp"}'
38
+ '{"origin": "127.0.0.1"}'
34
39
  ]
35
40
  end
36
41
 
@@ -38,13 +43,13 @@ class ClientTest < Test::Unit::TestCase
38
43
  # stubs.get('/unused') { [404, {}, ''] }
39
44
 
40
45
  cli = client(stubs)
41
- assert_equal 'shrimp', cli.sushi('ebi')
46
+ assert_equal '127.0.0.1', cli.httpbingo('api')
42
47
  stubs.verify_stubbed_calls
43
48
  end
44
49
 
45
- def test_sushi_404
50
+ def test_httpbingo_not_found
46
51
  stubs = Faraday::Adapter::Test::Stubs.new
47
- stubs.get('/ebi') do
52
+ stubs.get('/api') do
48
53
  [
49
54
  404,
50
55
  { 'Content-Type': 'application/javascript' },
@@ -53,20 +58,80 @@ class ClientTest < Test::Unit::TestCase
53
58
  end
54
59
 
55
60
  cli = client(stubs)
56
- assert_nil cli.sushi('ebi')
61
+ assert_nil cli.httpbingo('api')
57
62
  stubs.verify_stubbed_calls
58
63
  end
59
64
 
60
- def test_sushi_exception
65
+ def test_httpbingo_exception
61
66
  stubs = Faraday::Adapter::Test::Stubs.new
62
- stubs.get('/ebi') do
63
- raise Faraday::ConnectionFailed, nil
67
+ stubs.get('/api') do
68
+ raise Faraday::ConnectionFailed
64
69
  end
65
70
 
66
71
  cli = client(stubs)
67
72
  assert_raise Faraday::ConnectionFailed do
68
- cli.sushi('ebi')
73
+ cli.httpbingo('api')
74
+ end
75
+ stubs.verify_stubbed_calls
76
+ end
77
+
78
+ def test_strict_mode
79
+ stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
80
+ stubs.get('/api?abc=123') do
81
+ [
82
+ 200,
83
+ { 'Content-Type': 'application/javascript' },
84
+ '{"origin": "127.0.0.1"}'
85
+ ]
69
86
  end
87
+
88
+ cli = client(stubs)
89
+ assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123 })
90
+
91
+ # uncomment to raise Stubs::NotFound
92
+ # assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123, foo: 'Kappa' })
93
+ stubs.verify_stubbed_calls
94
+ end
95
+
96
+ def test_non_default_params_encoder
97
+ stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
98
+ stubs.get('/api?a=x&a=y&a=z') do
99
+ [
100
+ 200,
101
+ { 'Content-Type': 'application/javascript' },
102
+ '{"origin": "127.0.0.1"}'
103
+ ]
104
+ end
105
+ conn = Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder|
106
+ builder.adapter :test, stubs
107
+ end
108
+
109
+ cli = Client.new(conn)
110
+ assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y z] })
111
+
112
+ # uncomment to raise Stubs::NotFound
113
+ # assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y] })
114
+ stubs.verify_stubbed_calls
115
+ end
116
+
117
+ def test_with_string_body
118
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
119
+ stub.post('/foo', '{"name":"YK"}') { [200, {}, ''] }
120
+ end
121
+ cli = client(stubs)
122
+ assert_equal 200, cli.foo(name: 'YK')
123
+
124
+ stubs.verify_stubbed_calls
125
+ end
126
+
127
+ def test_with_proc_body
128
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
129
+ check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }
130
+ stub.post('/foo', check) { [200, {}, ''] }
131
+ end
132
+ cli = client(stubs)
133
+ assert_equal 200, cli.foo(name: 'YK', created_at: Time.now)
134
+
70
135
  stubs.verify_stubbed_calls
71
136
  end
72
137
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'timeout'
4
+
3
5
  module Faraday
4
6
  class Adapter
5
7
  # @example
@@ -25,6 +27,18 @@ module Faraday
25
27
  # "showing item: #{meta[:match_data][1]}"
26
28
  # ]
27
29
  # end
30
+ #
31
+ # # Test the request body is the same as the stubbed body
32
+ # stub.post('/bar', 'name=YK&word=call') { [200, {}, ''] }
33
+ #
34
+ # # You can pass a proc as a stubbed body and check the request body in your way.
35
+ # # In this case, the proc should return true or false.
36
+ # stub.post('/foo', ->(request_body) do
37
+ # JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }) { [200, {}, '']
38
+ # end
39
+ #
40
+ # # You can set strict_mode to exactly match the stubbed requests.
41
+ # stub.strict_mode = true
28
42
  # end
29
43
  # end
30
44
  #
@@ -39,6 +53,12 @@ module Faraday
39
53
  #
40
54
  # resp = test.get '/items/2'
41
55
  # resp.body # => 'showing item: 2'
56
+ #
57
+ # resp = test.post '/bar', 'name=YK&word=call'
58
+ # resp.status # => 200
59
+ #
60
+ # resp = test.post '/foo', JSON.dump(name: 'YK', created_at: Time.now)
61
+ # resp.status # => 200
42
62
  class Test < Faraday::Adapter
43
63
  attr_accessor :stubs
44
64
 
@@ -47,10 +67,12 @@ module Faraday
47
67
  class NotFound < StandardError
48
68
  end
49
69
 
50
- def initialize
70
+ def initialize(strict_mode: false)
51
71
  # { get: [Stub, Stub] }
52
72
  @stack = {}
53
73
  @consumed = {}
74
+ @strict_mode = strict_mode
75
+ @stubs_mutex = Monitor.new
54
76
  yield(self) if block_given?
55
77
  end
56
78
 
@@ -58,18 +80,23 @@ module Faraday
58
80
  @stack.empty?
59
81
  end
60
82
 
61
- def match(request_method, host, path, headers, body)
83
+ # @param env [Faraday::Env]
84
+ def match(env)
85
+ request_method = env[:method]
62
86
  return false unless @stack.key?(request_method)
63
87
 
64
88
  stack = @stack[request_method]
65
89
  consumed = (@consumed[request_method] ||= [])
66
90
 
67
- stub, meta = matches?(stack, host, path, headers, body)
68
- if stub
69
- consumed << stack.delete(stub)
70
- return stub, meta
91
+ @stubs_mutex.synchronize do
92
+ stub, meta = matches?(stack, env)
93
+ if stub
94
+ removed = stack.delete(stub)
95
+ consumed << removed unless removed.nil?
96
+ return stub, meta
97
+ end
71
98
  end
72
- matches?(consumed, host, path, headers, body)
99
+ matches?(consumed, env)
73
100
  end
74
101
 
75
102
  def get(path, headers = {}, &block)
@@ -115,6 +142,17 @@ module Faraday
115
142
  raise failed_stubs.join(' ') unless failed_stubs.empty?
116
143
  end
117
144
 
145
+ # Set strict_mode. If the value is true, this adapter tries to find matched requests strictly,
146
+ # which means that all of a path, parameters, and headers must be the same as an actual request.
147
+ def strict_mode=(value)
148
+ @strict_mode = value
149
+ @stack.each_value do |stubs|
150
+ stubs.each do |stub|
151
+ stub.strict_mode = value
152
+ end
153
+ end
154
+ end
155
+
118
156
  protected
119
157
 
120
158
  def new_stub(request_method, path, headers = {}, body = nil, &block)
@@ -127,14 +165,18 @@ module Faraday
127
165
  Faraday::Utils.URI(path).host
128
166
  ]
129
167
  end
168
+ path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path
169
+ headers = Utils::Headers.new(headers)
130
170
 
131
- stub = Stub.new(host, normalized_path, headers, body, block)
171
+ stub = Stub.new(host, path, query, headers, body, @strict_mode, block)
132
172
  (@stack[request_method] ||= []) << stub
133
173
  end
134
174
 
135
- def matches?(stack, host, path, headers, body)
175
+ # @param stack [Hash]
176
+ # @param env [Faraday::Env]
177
+ def matches?(stack, env)
136
178
  stack.each do |stub|
137
- match_result, meta = stub.matches?(host, path, headers, body)
179
+ match_result, meta = stub.matches?(env)
138
180
  return stub, meta if match_result
139
181
  end
140
182
  nil
@@ -142,36 +184,21 @@ module Faraday
142
184
  end
143
185
 
144
186
  # Stub request
145
- # rubocop:disable Style/StructInheritance
146
- class Stub < Struct.new(:host, :path, :params, :headers, :body, :block)
147
- # rubocop:enable Style/StructInheritance
148
- def initialize(host, full, headers, body, block)
149
- path, query = full.respond_to?(:split) ? full.split('?') : full
150
- params =
151
- if query
152
- Faraday::Utils.parse_nested_query(query)
153
- else
154
- {}
155
- end
187
+ Stub = Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) do
188
+ # @param env [Faraday::Env]
189
+ def matches?(env)
190
+ request_host = env[:url].host
191
+ request_path = Faraday::Utils.normalize_path(env[:url].path)
192
+ request_headers = env.request_headers
193
+ request_body = env[:body]
156
194
 
157
- super(host, path, params, headers, body, block)
158
- end
159
-
160
- def matches?(request_host, request_uri, request_headers, request_body)
161
- request_path, request_query = request_uri.split('?')
162
- request_params =
163
- if request_query
164
- Faraday::Utils.parse_nested_query(request_query)
165
- else
166
- {}
167
- end
168
195
  # meta is a hash used as carrier
169
196
  # that will be yielded to consumer block
170
197
  meta = {}
171
198
  [(host.nil? || host == request_host) &&
172
199
  path_match?(request_path, meta) &&
173
- params_match?(request_params) &&
174
- (body.to_s.size.zero? || request_body == body) &&
200
+ params_match?(env) &&
201
+ body_match?(request_body) &&
175
202
  headers_match?(request_headers), meta]
176
203
  end
177
204
 
@@ -183,18 +210,46 @@ module Faraday
183
210
  end
184
211
  end
185
212
 
186
- def params_match?(request_params)
213
+ # @param env [Faraday::Env]
214
+ def params_match?(env)
215
+ request_params = env[:params]
216
+ params = env.params_encoder.decode(query) || {}
217
+
218
+ if strict_mode
219
+ return Set.new(params) == Set.new(request_params)
220
+ end
221
+
187
222
  params.keys.all? do |key|
188
223
  request_params[key] == params[key]
189
224
  end
190
225
  end
191
226
 
192
227
  def headers_match?(request_headers)
228
+ if strict_mode
229
+ headers_with_user_agent = headers.dup.tap do |hs|
230
+ # NOTE: Set User-Agent in case it's not set when creating Stubs.
231
+ # Users would not want to set Faraday's User-Agent explicitly.
232
+ hs[:user_agent] ||= Connection::USER_AGENT
233
+ end
234
+ return Set.new(headers_with_user_agent) == Set.new(request_headers)
235
+ end
236
+
193
237
  headers.keys.all? do |key|
194
238
  request_headers[key] == headers[key]
195
239
  end
196
240
  end
197
241
 
242
+ def body_match?(request_body)
243
+ return true if body.to_s.empty?
244
+
245
+ case body
246
+ when Proc
247
+ body.call(request_body)
248
+ else
249
+ request_body == body
250
+ end
251
+ end
252
+
198
253
  def to_s
199
254
  "#{path} #{body}"
200
255
  end
@@ -210,37 +265,47 @@ module Faraday
210
265
  yield(stubs)
211
266
  end
212
267
 
268
+ # @param env [Faraday::Env]
213
269
  def call(env)
214
270
  super
215
- host = env[:url].host
216
- normalized_path = Faraday::Utils.normalize_path(env[:url])
217
- params_encoder = env.request.params_encoder ||
218
- Faraday::Utils.default_params_encoder
219
271
 
220
- stub, meta = stubs.match(env[:method], host, normalized_path,
221
- env.request_headers, env[:body])
272
+ env.request.params_encoder ||= Faraday::Utils.default_params_encoder
273
+ env[:params] = env.params_encoder.decode(env[:url].query) || {}
274
+ stub, meta = stubs.match(env)
222
275
 
223
276
  unless stub
224
- raise Stubs::NotFound, "no stubbed request for #{env[:method]} "\
225
- "#{normalized_path} #{env[:body]}"
277
+ raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
278
+ "#{env[:url]} #{env[:body]} #{env[:headers]}"
226
279
  end
227
280
 
228
- env[:params] = if (query = env[:url].query)
229
- params_encoder.decode(query)
230
- else
231
- {}
232
- end
233
281
  block_arity = stub.block.arity
282
+ params = if block_arity >= 0
283
+ [env, meta].take(block_arity)
284
+ else
285
+ [env, meta]
286
+ end
287
+
288
+ timeout = request_timeout(:open, env[:request])
289
+ timeout ||= request_timeout(:read, env[:request])
290
+
234
291
  status, headers, body =
235
- if block_arity >= 0
236
- stub.block.call(*[env, meta].take(block_arity))
292
+ if timeout
293
+ ::Timeout.timeout(timeout, Faraday::TimeoutError) do
294
+ stub.block.call(*params)
295
+ end
237
296
  else
238
- stub.block.call(env, meta)
297
+ stub.block.call(*params)
239
298
  end
240
- save_response(env, status, body, headers)
299
+
300
+ # We need to explicitly pass `reason_phrase = nil` here to avoid keyword args conflicts.
301
+ # See https://github.com/lostisland/faraday/issues/1444
302
+ # TODO: remove `nil` explicit reason_phrase once Ruby 3.0 becomes minimum req. version
303
+ save_response(env, status, body, headers, nil)
241
304
 
242
305
  @app.call(env)
243
306
  end
244
307
  end
245
308
  end
246
309
  end
310
+
311
+ Faraday::Adapter.register_middleware(test: Faraday::Adapter::Test)
@@ -5,28 +5,13 @@ module Faraday
5
5
  # responsible for fulfilling a Faraday request.
6
6
  class Adapter
7
7
  extend MiddlewareRegistry
8
- extend DependencyLoader
9
8
 
10
9
  CONTENT_LENGTH = 'Content-Length'
11
10
 
12
- register_middleware File.expand_path('adapter', __dir__),
13
- test: [:Test, 'test'],
14
- net_http: [:NetHttp, 'net_http'],
15
- net_http_persistent: [
16
- :NetHttpPersistent,
17
- 'net_http_persistent'
18
- ],
19
- typhoeus: [:Typhoeus, 'typhoeus'],
20
- patron: [:Patron, 'patron'],
21
- em_synchrony: [:EMSynchrony, 'em_synchrony'],
22
- em_http: [:EMHttp, 'em_http'],
23
- excon: [:Excon, 'excon'],
24
- rack: [:Rack, 'rack'],
25
- httpclient: [:HTTPClient, 'httpclient']
26
-
27
11
  # This module marks an Adapter as supporting parallel requests.
28
12
  module Parallelism
29
13
  attr_writer :supports_parallel
14
+
30
15
  def supports_parallel?
31
16
  @supports_parallel
32
17
  end
@@ -74,7 +59,7 @@ module Faraday
74
59
 
75
60
  private
76
61
 
77
- def save_response(env, status, body, headers = nil, reason_phrase = nil)
62
+ def save_response(env, status, body, headers = nil, reason_phrase = nil, finished: true)
78
63
  env.status = status
79
64
  env.body = body
80
65
  env.reason_phrase = reason_phrase&.to_s&.strip
@@ -83,7 +68,7 @@ module Faraday
83
68
  yield(response_headers) if block_given?
84
69
  end
85
70
 
86
- env.response.finish(env) unless env.parallel?
71
+ env.response.finish(env) unless env.parallel? || !finished
87
72
  env.response
88
73
  end
89
74
 
@@ -93,8 +78,7 @@ module Faraday
93
78
  # @param type [Symbol] Describes which timeout setting to get: :read,
94
79
  # :write, or :open.
95
80
  # @param options [Hash] Hash containing Symbol keys like :timeout,
96
- # :read_timeout, :write_timeout, :open_timeout, or
97
- # :timeout
81
+ # :read_timeout, :write_timeout, or :open_timeout
98
82
  #
99
83
  # @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
100
84
  # has been set.
@@ -113,3 +97,5 @@ module Faraday
113
97
  }.freeze
114
98
  end
115
99
  end
100
+
101
+ require 'faraday/adapter/test'
@@ -12,7 +12,9 @@ module Faraday
12
12
  end
13
13
 
14
14
  def get(name)
15
- klass = @constants[name]
15
+ klass = @lock.synchronize do
16
+ @constants[name]
17
+ end
16
18
  return klass if klass
17
19
 
18
20
  Object.const_get(name).tap { |c| set(c, name) }