faraday 1.1.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -1
  3. data/LICENSE.md +1 -1
  4. data/README.md +34 -21
  5. data/Rakefile +3 -1
  6. data/examples/client_spec.rb +67 -13
  7. data/examples/client_test.rb +80 -15
  8. data/lib/faraday/adapter/test.rb +117 -52
  9. data/lib/faraday/adapter.rb +5 -20
  10. data/lib/faraday/connection.rb +70 -129
  11. data/lib/faraday/encoders/nested_params_encoder.rb +14 -7
  12. data/lib/faraday/error.rb +29 -8
  13. data/lib/faraday/logging/formatter.rb +28 -15
  14. data/lib/faraday/methods.rb +6 -0
  15. data/lib/faraday/middleware.rb +17 -5
  16. data/lib/faraday/middleware_registry.rb +17 -63
  17. data/lib/faraday/options/connection_options.rb +7 -6
  18. data/lib/faraday/options/env.rb +85 -62
  19. data/lib/faraday/options/proxy_options.rb +11 -3
  20. data/lib/faraday/options/request_options.rb +7 -6
  21. data/lib/faraday/options/ssl_options.rb +56 -45
  22. data/lib/faraday/options.rb +7 -6
  23. data/lib/faraday/rack_builder.rb +23 -21
  24. data/lib/faraday/request/authorization.rb +37 -38
  25. data/lib/faraday/request/instrumentation.rb +5 -1
  26. data/lib/faraday/request/json.rb +70 -0
  27. data/lib/faraday/request/url_encoded.rb +5 -1
  28. data/lib/faraday/request.rb +20 -37
  29. data/lib/faraday/response/json.rb +73 -0
  30. data/lib/faraday/response/logger.rb +8 -4
  31. data/lib/faraday/response/raise_error.rb +33 -6
  32. data/lib/faraday/response.rb +10 -26
  33. data/lib/faraday/utils/headers.rb +7 -2
  34. data/lib/faraday/utils.rb +11 -7
  35. data/lib/faraday/version.rb +5 -0
  36. data/lib/faraday.rb +49 -58
  37. data/spec/faraday/adapter/test_spec.rb +182 -0
  38. data/spec/faraday/connection_spec.rb +207 -90
  39. data/spec/faraday/error_spec.rb +45 -5
  40. data/spec/faraday/middleware_registry_spec.rb +31 -0
  41. data/spec/faraday/middleware_spec.rb +50 -6
  42. data/spec/faraday/options/env_spec.rb +8 -2
  43. data/spec/faraday/options/options_spec.rb +1 -1
  44. data/spec/faraday/options/proxy_options_spec.rb +15 -0
  45. data/spec/faraday/params_encoders/nested_spec.rb +8 -0
  46. data/spec/faraday/rack_builder_spec.rb +26 -54
  47. data/spec/faraday/request/authorization_spec.rb +54 -24
  48. data/spec/faraday/request/instrumentation_spec.rb +5 -7
  49. data/spec/faraday/request/json_spec.rb +199 -0
  50. data/spec/faraday/request/url_encoded_spec.rb +12 -2
  51. data/spec/faraday/request_spec.rb +5 -15
  52. data/spec/faraday/response/json_spec.rb +189 -0
  53. data/spec/faraday/response/logger_spec.rb +38 -0
  54. data/spec/faraday/response/raise_error_spec.rb +77 -5
  55. data/spec/faraday/response_spec.rb +3 -1
  56. data/spec/faraday/utils/headers_spec.rb +22 -4
  57. data/spec/faraday/utils_spec.rb +63 -1
  58. data/spec/faraday_spec.rb +8 -4
  59. data/spec/spec_helper.rb +6 -5
  60. data/spec/support/fake_safe_buffer.rb +1 -1
  61. data/spec/support/helper_methods.rb +0 -37
  62. data/spec/support/shared_examples/adapter.rb +4 -3
  63. data/spec/support/shared_examples/request_method.rb +58 -29
  64. metadata +17 -57
  65. data/lib/faraday/adapter/em_http.rb +0 -286
  66. data/lib/faraday/adapter/em_http_ssl_patch.rb +0 -62
  67. data/lib/faraday/adapter/em_synchrony/parallel_manager.rb +0 -69
  68. data/lib/faraday/adapter/em_synchrony.rb +0 -150
  69. data/lib/faraday/adapter/excon.rb +0 -124
  70. data/lib/faraday/adapter/httpclient.rb +0 -152
  71. data/lib/faraday/adapter/net_http.rb +0 -219
  72. data/lib/faraday/adapter/net_http_persistent.rb +0 -91
  73. data/lib/faraday/adapter/patron.rb +0 -132
  74. data/lib/faraday/adapter/rack.rb +0 -75
  75. data/lib/faraday/adapter/typhoeus.rb +0 -15
  76. data/lib/faraday/autoload.rb +0 -95
  77. data/lib/faraday/dependency_loader.rb +0 -39
  78. data/lib/faraday/file_part.rb +0 -128
  79. data/lib/faraday/param_part.rb +0 -53
  80. data/lib/faraday/request/basic_authentication.rb +0 -20
  81. data/lib/faraday/request/multipart.rb +0 -106
  82. data/lib/faraday/request/retry.rb +0 -239
  83. data/lib/faraday/request/token_authentication.rb +0 -20
  84. data/spec/faraday/adapter/em_http_spec.rb +0 -47
  85. data/spec/faraday/adapter/em_synchrony_spec.rb +0 -16
  86. data/spec/faraday/adapter/excon_spec.rb +0 -49
  87. data/spec/faraday/adapter/httpclient_spec.rb +0 -73
  88. data/spec/faraday/adapter/net_http_persistent_spec.rb +0 -57
  89. data/spec/faraday/adapter/net_http_spec.rb +0 -64
  90. data/spec/faraday/adapter/patron_spec.rb +0 -18
  91. data/spec/faraday/adapter/rack_spec.rb +0 -8
  92. data/spec/faraday/adapter/typhoeus_spec.rb +0 -7
  93. data/spec/faraday/composite_read_io_spec.rb +0 -80
  94. data/spec/faraday/request/multipart_spec.rb +0 -302
  95. data/spec/faraday/request/retry_spec.rb +0 -242
  96. data/spec/faraday/response/middleware_spec.rb +0 -68
  97. data/spec/support/webmock_rack_app.rb +0 -68
data/lib/faraday.rb CHANGED
@@ -1,11 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cgi'
4
+ require 'date'
4
5
  require 'set'
5
6
  require 'forwardable'
7
+ require 'faraday/version'
8
+ require 'faraday/methods'
9
+ require 'faraday/error'
6
10
  require 'faraday/middleware_registry'
7
- require 'faraday/dependency_loader'
8
-
11
+ require 'faraday/utils'
12
+ require 'faraday/options'
13
+ require 'faraday/connection'
14
+ require 'faraday/rack_builder'
15
+ require 'faraday/parameters'
16
+ require 'faraday/middleware'
17
+ require 'faraday/adapter'
18
+ require 'faraday/request'
19
+ require 'faraday/response'
20
+ require 'faraday/net_http'
9
21
  # This is the main namespace for Faraday.
10
22
  #
11
23
  # It provides methods to create {Connection} objects, and HTTP-related
@@ -19,9 +31,7 @@ require 'faraday/dependency_loader'
19
31
  # conn.get '/'
20
32
  #
21
33
  module Faraday
22
- VERSION = '1.1.0'
23
- METHODS_WITH_QUERY = %w[get head delete trace].freeze
24
- METHODS_WITH_BODY = %w[post put patch].freeze
34
+ CONTENT_TYPE = 'Content-Type'
25
35
 
26
36
  class << self
27
37
  # The root path that Faraday is being loaded from.
@@ -44,6 +54,10 @@ module Faraday
44
54
  # @return [Symbol] the new default_adapter.
45
55
  attr_reader :default_adapter
46
56
 
57
+ # Option for the default_adapter
58
+ # @return [Hash] default_adapter options
59
+ attr_accessor :default_adapter_options
60
+
47
61
  # Documented below, see default_connection
48
62
  attr_writer :default_connection
49
63
 
@@ -80,23 +94,10 @@ module Faraday
80
94
  # params: { page: 1 }
81
95
  # # => Faraday::Connection to http://faraday.com?page=1
82
96
  def new(url = nil, options = {}, &block)
83
- options = default_connection_options.merge(options)
97
+ options = Utils.deep_merge(default_connection_options, options)
84
98
  Faraday::Connection.new(url, options, &block)
85
99
  end
86
100
 
87
- # @private
88
- # Internal: Requires internal Faraday libraries.
89
- #
90
- # @param libs [Array] one or more relative String names to Faraday classes.
91
- # @return [void]
92
- def require_libs(*libs)
93
- libs.each do |lib|
94
- require "#{lib_path}/#{lib}"
95
- end
96
- end
97
-
98
- alias require_lib require_libs
99
-
100
101
  # Documented elsewhere, see default_adapter reader
101
102
  def default_adapter=(adapter)
102
103
  @default_connection = nil
@@ -107,6 +108,34 @@ module Faraday
107
108
  default_connection.respond_to?(symbol, include_private) || super
108
109
  end
109
110
 
111
+ # @overload default_connection
112
+ # Gets the default connection used for simple scripts.
113
+ # @return [Faraday::Connection] a connection configured with
114
+ # the default_adapter.
115
+ # @overload default_connection=(connection)
116
+ # @param connection [Faraday::Connection]
117
+ # Sets the default {Faraday::Connection} for simple scripts that
118
+ # access the Faraday constant directly, such as
119
+ # <code>Faraday.get "https://faraday.com"</code>.
120
+ def default_connection
121
+ @default_connection ||= Connection.new(default_connection_options)
122
+ end
123
+
124
+ # Gets the default connection options used when calling {Faraday#new}.
125
+ #
126
+ # @return [Faraday::ConnectionOptions]
127
+ def default_connection_options
128
+ @default_connection_options ||= ConnectionOptions.new
129
+ end
130
+
131
+ # Sets the default options used when calling {Faraday#new}.
132
+ #
133
+ # @param options [Hash, Faraday::ConnectionOptions]
134
+ def default_connection_options=(options)
135
+ @default_connection = nil
136
+ @default_connection_options = ConnectionOptions.from(options)
137
+ end
138
+
110
139
  private
111
140
 
112
141
  # Internal: Proxies method calls on the Faraday constant to
@@ -124,43 +153,5 @@ module Faraday
124
153
  self.root_path = File.expand_path __dir__
125
154
  self.lib_path = File.expand_path 'faraday', __dir__
126
155
  self.default_adapter = :net_http
127
-
128
- # @overload default_connection
129
- # Gets the default connection used for simple scripts.
130
- # @return [Faraday::Connection] a connection configured with
131
- # the default_adapter.
132
- # @overload default_connection=(connection)
133
- # @param connection [Faraday::Connection]
134
- # Sets the default {Faraday::Connection} for simple scripts that
135
- # access the Faraday constant directly, such as
136
- # <code>Faraday.get "https://faraday.com"</code>.
137
- def self.default_connection
138
- @default_connection ||= Connection.new(default_connection_options)
139
- end
140
-
141
- # Gets the default connection options used when calling {Faraday#new}.
142
- #
143
- # @return [Faraday::ConnectionOptions]
144
- def self.default_connection_options
145
- @default_connection_options ||= ConnectionOptions.new
146
- end
147
-
148
- # Sets the default options used when calling {Faraday#new}.
149
- #
150
- # @param options [Hash, Faraday::ConnectionOptions]
151
- def self.default_connection_options=(options)
152
- @default_connection = nil
153
- @default_connection_options = ConnectionOptions.from(options)
154
- end
155
-
156
- unless defined?(::Faraday::Timer)
157
- require 'timeout'
158
- Timer = Timeout
159
- end
160
-
161
- require_libs 'utils', 'options', 'connection', 'rack_builder', 'parameters',
162
- 'middleware', 'adapter', 'request', 'response', 'error',
163
- 'file_part', 'param_part'
164
-
165
- require_lib 'autoload' unless ENV['FARADAY_NO_AUTOLOAD']
156
+ self.default_adapter_options = {}
166
157
  end
@@ -257,4 +257,186 @@ RSpec.describe Faraday::Adapter::Test do
257
257
  it { expect { request }.to raise_error described_class::Stubs::NotFound }
258
258
  end
259
259
  end
260
+
261
+ describe 'for request with non default params encoder' do
262
+ let(:connection) do
263
+ Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder|
264
+ builder.adapter :test, stubs
265
+ end
266
+ end
267
+ let(:stubs) do
268
+ described_class::Stubs.new do |stubs|
269
+ stubs.get('/path?a=x&a=y&a=z') { [200, {}, 'a'] }
270
+ end
271
+ end
272
+
273
+ context 'when all flat param values are correctly set' do
274
+ subject(:request) { connection.get('/path?a=x&a=y&a=z') }
275
+
276
+ it { expect(request.status).to eq 200 }
277
+ end
278
+
279
+ shared_examples 'raise NotFound when params do not satisfy the flat param values' do |params|
280
+ subject(:request) { connection.get('/path', params) }
281
+
282
+ context "with #{params.inspect}" do
283
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
284
+ end
285
+ end
286
+
287
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x] }
288
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x y] }
289
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { a: %w[x z y] } # NOTE: The order of the value is also compared.
290
+ it_behaves_like 'raise NotFound when params do not satisfy the flat param values', { b: %w[x y z] }
291
+ end
292
+
293
+ describe 'strict_mode' do
294
+ let(:stubs) do
295
+ described_class::Stubs.new(strict_mode: true) do |stubs|
296
+ stubs.get('/strict?a=12&b=xy', 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello') { [200, {}, 'a'] }
297
+ stubs.get('/with_user_agent?a=12&b=xy', authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent') { [200, {}, 'a'] }
298
+ end
299
+ end
300
+
301
+ context 'when params and headers are exactly set' do
302
+ subject(:request) { connection.get('/strict', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', x_c: 'hello' }) }
303
+
304
+ it { expect(request.status).to eq 200 }
305
+ end
306
+
307
+ context 'when params and headers are exactly set with a custom user agent' do
308
+ subject(:request) { connection.get('/with_user_agent', { a: '12', b: 'xy' }, { authorization: 'Bearer m_ck', 'User-Agent' => 'My Agent' }) }
309
+
310
+ it { expect(request.status).to eq 200 }
311
+ end
312
+
313
+ shared_examples 'raise NotFound when params do not satisfy the strict check' do |params|
314
+ subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) }
315
+
316
+ context "with #{params.inspect}" do
317
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
318
+ end
319
+ end
320
+
321
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12' }
322
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { b: 'xy' }
323
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '123', b: 'xy' }
324
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xyz' }
325
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' }
326
+ it_behaves_like 'raise NotFound when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' }
327
+
328
+ shared_examples 'raise NotFound when headers do not satisfy the strict check' do |path, headers|
329
+ subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) }
330
+
331
+ context "with #{headers.inspect}" do
332
+ it { expect { request }.to raise_error described_class::Stubs::NotFound }
333
+ end
334
+ end
335
+
336
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck' }
337
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { 'X-C' => 'hello' }
338
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'Hi' }
339
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Basic m_ck', 'x-c': 'hello' }
340
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' }
341
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck' }
342
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'Unknown' }
343
+ it_behaves_like 'raise NotFound when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
344
+
345
+ context 'when strict_mode is disabled' do
346
+ before do
347
+ stubs.strict_mode = false
348
+ end
349
+
350
+ shared_examples 'does not raise NotFound even when params do not satisfy the strict check' do |params|
351
+ subject(:request) { connection.get('/strict', params, { 'Authorization' => 'Bearer m_ck', 'X-C' => 'hello' }) }
352
+
353
+ context "with #{params.inspect}" do
354
+ it { expect(request.status).to eq 200 }
355
+ end
356
+ end
357
+
358
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy' }
359
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { a: '12', b: 'xy', c: 'hello' }
360
+ it_behaves_like 'does not raise NotFound even when params do not satisfy the strict check', { additional: 'special', a: '12', b: 'xy', c: 'hello' }
361
+
362
+ shared_examples 'does not raise NotFound even when headers do not satisfy the strict check' do |path, headers|
363
+ subject(:request) { connection.get(path, { a: 12, b: 'xy' }, headers) }
364
+
365
+ context "with #{headers.inspect}" do
366
+ it { expect(request.status).to eq 200 }
367
+ end
368
+ end
369
+
370
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello' }
371
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', x_special: 'special' }
372
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/strict', { authorization: 'Bearer m_ck', 'x-c': 'hello', user_agent: 'Special Agent' }
373
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent' }
374
+ it_behaves_like 'does not raise NotFound even when headers do not satisfy the strict check', '/with_user_agent', { authorization: 'Bearer m_ck', user_agent: 'My Agent', x_special: 'special' }
375
+ end
376
+
377
+ describe 'body_match?' do
378
+ let(:stubs) do
379
+ described_class::Stubs.new do |stubs|
380
+ stubs.post('/no_check') { [200, {}, 'ok'] }
381
+ stubs.post('/with_string', 'abc') { [200, {}, 'ok'] }
382
+ stubs.post(
383
+ '/with_proc',
384
+ ->(request_body) { JSON.parse(request_body, symbolize_names: true) == { x: '!', a: [{ m: [{ a: true }], n: 123 }] } },
385
+ { content_type: 'application/json' }
386
+ ) do
387
+ [200, {}, 'ok']
388
+ end
389
+ end
390
+ end
391
+
392
+ context 'when trying without any args for body' do
393
+ subject(:without_body) { connection.post('/no_check') }
394
+
395
+ it { expect(without_body.status).to eq 200 }
396
+ end
397
+
398
+ context 'when trying with string body stubs' do
399
+ subject(:with_string) { connection.post('/with_string', 'abc') }
400
+
401
+ it { expect(with_string.status).to eq 200 }
402
+ end
403
+
404
+ context 'when trying with proc body stubs' do
405
+ subject(:with_proc) do
406
+ connection.post('/with_proc', JSON.dump(a: [{ n: 123, m: [{ a: true }] }], x: '!'), { 'Content-Type' => 'application/json' })
407
+ end
408
+
409
+ it { expect(with_proc.status).to eq 200 }
410
+ end
411
+ end
412
+ end
413
+
414
+ describe 'request timeout' do
415
+ subject(:request) do
416
+ connection.get('/sleep') do |req|
417
+ req.options.timeout = timeout
418
+ end
419
+ end
420
+
421
+ before do
422
+ stubs.get('/sleep') do
423
+ sleep(0.01)
424
+ [200, {}, '']
425
+ end
426
+ end
427
+
428
+ context 'when request is within timeout' do
429
+ let(:timeout) { 1 }
430
+
431
+ it { expect(request.status).to eq 200 }
432
+ end
433
+
434
+ context 'when request is too slow' do
435
+ let(:timeout) { 0.001 }
436
+
437
+ it 'raises an exception' do
438
+ expect { request }.to raise_error(Faraday::TimeoutError)
439
+ end
440
+ end
441
+ end
260
442
  end