faraday 0.17.0 → 0.17.1

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +216 -0
  3. data/Rakefile +13 -0
  4. data/lib/faraday.rb +1 -1
  5. data/lib/faraday/adapter/em_http.rb +9 -9
  6. data/lib/faraday/adapter/em_synchrony.rb +5 -5
  7. data/lib/faraday/adapter/excon.rb +3 -3
  8. data/lib/faraday/adapter/httpclient.rb +4 -4
  9. data/lib/faraday/adapter/net_http.rb +2 -2
  10. data/lib/faraday/adapter/net_http_persistent.rb +3 -3
  11. data/lib/faraday/adapter/patron.rb +5 -5
  12. data/lib/faraday/adapter/rack.rb +1 -1
  13. data/lib/faraday/deprecate.rb +101 -0
  14. data/lib/faraday/error.rb +85 -32
  15. data/lib/faraday/options.rb +1 -1
  16. data/lib/faraday/request/retry.rb +6 -5
  17. data/lib/faraday/response.rb +3 -3
  18. data/lib/faraday/response/raise_error.rb +7 -3
  19. data/spec/faraday/deprecate_spec.rb +69 -0
  20. data/spec/faraday/error_spec.rb +102 -0
  21. data/spec/faraday/response/raise_error_spec.rb +95 -0
  22. data/spec/spec_helper.rb +104 -0
  23. data/test/adapters/default_test.rb +14 -0
  24. data/test/adapters/em_http_test.rb +30 -0
  25. data/test/adapters/em_synchrony_test.rb +32 -0
  26. data/test/adapters/excon_test.rb +30 -0
  27. data/test/adapters/httpclient_test.rb +34 -0
  28. data/test/adapters/integration.rb +263 -0
  29. data/test/adapters/logger_test.rb +136 -0
  30. data/test/adapters/net_http_persistent_test.rb +114 -0
  31. data/test/adapters/net_http_test.rb +79 -0
  32. data/test/adapters/patron_test.rb +40 -0
  33. data/test/adapters/rack_test.rb +38 -0
  34. data/test/adapters/test_middleware_test.rb +157 -0
  35. data/test/adapters/typhoeus_test.rb +38 -0
  36. data/test/authentication_middleware_test.rb +65 -0
  37. data/test/composite_read_io_test.rb +109 -0
  38. data/test/connection_test.rb +738 -0
  39. data/test/env_test.rb +268 -0
  40. data/test/helper.rb +75 -0
  41. data/test/live_server.rb +67 -0
  42. data/test/middleware/instrumentation_test.rb +88 -0
  43. data/test/middleware/retry_test.rb +282 -0
  44. data/test/middleware_stack_test.rb +260 -0
  45. data/test/multibyte.txt +1 -0
  46. data/test/options_test.rb +333 -0
  47. data/test/parameters_test.rb +157 -0
  48. data/test/request_middleware_test.rb +126 -0
  49. data/test/response_middleware_test.rb +72 -0
  50. data/test/strawberry.rb +2 -0
  51. data/test/utils_test.rb +98 -0
  52. metadata +47 -5
@@ -1,21 +1,25 @@
1
- module Faraday
2
- class Error < StandardError; end
1
+ # frozen_string_literal: true
3
2
 
4
- class ClientError < Error
3
+ require 'faraday/deprecate'
4
+
5
+ # Faraday namespace.
6
+ module Faraday
7
+ # Faraday error base class.
8
+ class Error < StandardError
5
9
  attr_reader :response, :wrapped_exception
6
10
 
7
- def initialize(ex, response = nil)
11
+ def initialize(exc, response = nil)
8
12
  @wrapped_exception = nil
9
13
  @response = response
10
14
 
11
- if ex.respond_to?(:backtrace)
12
- super(ex.message)
13
- @wrapped_exception = ex
14
- elsif ex.respond_to?(:each_key)
15
- super("the server responded with status #{ex[:status]}")
16
- @response = ex
15
+ if exc.respond_to?(:backtrace)
16
+ super(exc.message)
17
+ @wrapped_exception = exc
18
+ elsif exc.respond_to?(:each_key)
19
+ super("the server responded with status #{exc[:status]}")
20
+ @response = exc
17
21
  else
18
- super(ex.to_s)
22
+ super(exc.to_s)
19
23
  end
20
24
  end
21
25
 
@@ -28,39 +32,88 @@ module Faraday
28
32
  end
29
33
 
30
34
  def inspect
31
- inner = ''
32
- if @wrapped_exception
33
- inner << " wrapped=#{@wrapped_exception.inspect}"
34
- end
35
- if @response
36
- inner << " response=#{@response.inspect}"
37
- end
38
- if inner.empty?
39
- inner << " #{super}"
40
- end
35
+ inner = +''
36
+ inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
37
+ inner << " response=#{@response.inspect}" if @response
38
+ inner << " #{super}" if inner.empty?
41
39
  %(#<#{self.class}#{inner}>)
42
40
  end
43
41
  end
44
42
 
45
- class ConnectionFailed < ClientError; end
46
- class ResourceNotFound < ClientError; end
47
- class ParsingError < ClientError; end
43
+ # Faraday client error class. Represents 4xx status responses.
44
+ class ClientError < Error
45
+ end
46
+
47
+ # Raised by Faraday::Response::RaiseError in case of a 400 response.
48
+ class BadRequestError < ClientError
49
+ end
50
+
51
+ # Raised by Faraday::Response::RaiseError in case of a 401 response.
52
+ class UnauthorizedError < ClientError
53
+ end
54
+
55
+ # Raised by Faraday::Response::RaiseError in case of a 403 response.
56
+ class ForbiddenError < ClientError
57
+ end
58
+
59
+ # Raised by Faraday::Response::RaiseError in case of a 404 response.
60
+ class ResourceNotFound < ClientError
61
+ end
62
+
63
+ # Raised by Faraday::Response::RaiseError in case of a 407 response.
64
+ class ProxyAuthError < ClientError
65
+ end
66
+
67
+ # Raised by Faraday::Response::RaiseError in case of a 409 response.
68
+ class ConflictError < ClientError
69
+ end
70
+
71
+ # Raised by Faraday::Response::RaiseError in case of a 422 response.
72
+ class UnprocessableEntityError < ClientError
73
+ end
74
+
75
+ # Faraday server error class. Represents 5xx status responses.
76
+ class ServerError < Error
77
+ end
78
+
79
+ # A unified client error for timeouts.
80
+ class TimeoutError < ServerError
81
+ def initialize(exc = 'timeout', response = nil)
82
+ super(exc, response)
83
+ end
84
+ end
48
85
 
49
- class TimeoutError < ClientError
50
- def initialize(ex = nil)
51
- super(ex || "timeout")
86
+ # Raised by Faraday::Response::RaiseError in case of a nil status in response.
87
+ class NilStatusError < ServerError
88
+ def initialize(_exc, response: nil)
89
+ message = 'http status could not be derived from the server response'
90
+ super(message, response)
52
91
  end
53
92
  end
54
93
 
55
- class SSLError < ClientError
94
+ # A unified error for failed connections.
95
+ class ConnectionFailed < Error
56
96
  end
57
97
 
58
- class RetriableResponse < ClientError; end
98
+ # A unified client error for SSL errors.
99
+ class SSLError < Error
100
+ end
59
101
 
60
- [:ClientError, :ConnectionFailed, :ResourceNotFound,
61
- :ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const|
62
- Error.const_set(const, Faraday.const_get(const))
102
+ # Raised by FaradayMiddleware::ResponseMiddleware
103
+ class ParsingError < Error
63
104
  end
64
105
 
106
+ # Exception used to control the Retry middleware.
107
+ #
108
+ # @see Faraday::Request::Retry
109
+ class RetriableResponse < Error
110
+ end
65
111
 
112
+ %i[ClientError ConnectionFailed ResourceNotFound
113
+ ParsingError TimeoutError SSLError RetriableResponse].each do |const|
114
+ Error.const_set(
115
+ const,
116
+ DeprecatedClass.proxy_class(Faraday.const_get(const))
117
+ )
118
+ end
66
119
  end
@@ -72,7 +72,7 @@ module Faraday
72
72
  if args.size > 0
73
73
  send(key_setter, args.first)
74
74
  elsif block_given?
75
- send(key_setter, Proc.new.call(key))
75
+ send(key_setter, yield(key))
76
76
  else
77
77
  raise self.class.fetch_error_class, "key not found: #{key.inspect}"
78
78
  end
@@ -21,8 +21,9 @@ module Faraday
21
21
  # interval that is random between 0.1 and 0.15
22
22
  #
23
23
  class Request::Retry < Faraday::Middleware
24
-
25
- DEFAULT_EXCEPTIONS = [Errno::ETIMEDOUT, 'Timeout::Error', Error::TimeoutError, Faraday::Error::RetriableResponse].freeze
24
+ DEFAULT_EXCEPTIONS = [Errno::ETIMEDOUT, 'Timeout::Error',
25
+ Faraday::TimeoutError, Faraday::RetriableResponse
26
+ ].freeze
26
27
  IDEMPOTENT_METHODS = [:delete, :get, :head, :options, :put]
27
28
 
28
29
  class Options < Faraday::Options.new(:max, :interval, :max_interval, :interval_randomness,
@@ -95,7 +96,7 @@ module Faraday
95
96
  # exceptions - The list of exceptions to handle. Exceptions can be
96
97
  # given as Class, Module, or String. (default:
97
98
  # [Errno::ETIMEDOUT, 'Timeout::Error',
98
- # Error::TimeoutError, Faraday::Error::RetriableResponse])
99
+ # Faraday::TimeoutError, Faraday::RetriableResponse])
99
100
  # methods - A list of HTTP methods to retry without calling retry_if. Pass
100
101
  # an empty Array to call retry_if for all exceptions.
101
102
  # (defaults to the idempotent HTTP methods in IDEMPOTENT_METHODS)
@@ -128,7 +129,7 @@ module Faraday
128
129
  begin
129
130
  env[:body] = request_body # after failure env[:body] is set to the response body
130
131
  @app.call(env).tap do |resp|
131
- raise Faraday::Error::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
132
+ raise Faraday::RetriableResponse.new(nil, resp) if @options.retry_statuses.include?(resp.status)
132
133
  end
133
134
  rescue @errmatch => exception
134
135
  if retries > 0 && retry_request?(env, exception)
@@ -141,7 +142,7 @@ module Faraday
141
142
  end
142
143
  end
143
144
 
144
- if exception.is_a?(Faraday::Error::RetriableResponse)
145
+ if exception.is_a?(Faraday::RetriableResponse)
145
146
  exception.response
146
147
  else
147
148
  raise
@@ -54,9 +54,9 @@ module Faraday
54
54
  !!env
55
55
  end
56
56
 
57
- def on_complete
58
- if not finished?
59
- @on_complete_callbacks << Proc.new
57
+ def on_complete(&block)
58
+ if !finished?
59
+ @on_complete_callbacks << block
60
60
  else
61
61
  yield(env)
62
62
  end
@@ -5,12 +5,16 @@ module Faraday
5
5
  def on_complete(env)
6
6
  case env[:status]
7
7
  when 404
8
- raise Faraday::Error::ResourceNotFound, response_values(env)
8
+ raise Faraday::ResourceNotFound, response_values(env)
9
9
  when 407
10
10
  # mimic the behavior that we get with proxy requests with HTTPS
11
- raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
11
+ raise Faraday::ConnectionFailed.new(
12
+ %{407 "Proxy Authentication Required "},
13
+ response_values(env))
12
14
  when ClientErrorStatuses
13
- raise Faraday::Error::ClientError, response_values(env)
15
+ raise Faraday::ClientError, response_values(env)
16
+ when nil
17
+ raise Faraday::NilStatusError, response: response_values(env)
14
18
  end
15
19
  end
16
20
 
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::DeprecatedClass do
4
+ class SampleClass < StandardError
5
+ attr_accessor :foo
6
+
7
+ def initialize(foo = nil)
8
+ @foo = foo || :foo
9
+ end
10
+ end
11
+
12
+ SampleDeprecatedClass = Faraday::DeprecatedClass.proxy_class(SampleClass)
13
+
14
+ it 'does not raise error for deprecated classes but prints an error message' do
15
+ error_message, foobar = with_warn_squelching { SampleDeprecatedClass.new(:foo_bar) }
16
+ expect(foobar).to be_a(SampleClass)
17
+ expect(foobar.foo).to eq(:foo_bar)
18
+ expect(error_message).to match(
19
+ Regexp.new(
20
+ 'NOTE: SampleDeprecatedClass.new is deprecated; '\
21
+ 'use SampleClass.new instead. It will be removed in or after version 1.0'
22
+ )
23
+ )
24
+ end
25
+
26
+ it 'does not raise an error for inherited error-namespaced classes but prints an error message' do
27
+ error_message, = with_warn_squelching { Class.new(SampleDeprecatedClass) }
28
+
29
+ expect(error_message).to match(
30
+ Regexp.new(
31
+ 'NOTE: Inheriting SampleDeprecatedClass is deprecated; '\
32
+ 'use SampleClass instead. It will be removed in or after version 1.0'
33
+ )
34
+ )
35
+ end
36
+
37
+ it 'allows backward-compatible class to be subclassed' do
38
+ expect {
39
+ with_warn_squelching { Class.new(SampleDeprecatedClass) }
40
+ }.not_to raise_error
41
+ end
42
+
43
+ it 'allows rescuing of a current error with a deprecated error' do
44
+ expect { raise SampleClass, nil }.to raise_error(SampleDeprecatedClass)
45
+ end
46
+
47
+ it 'allows rescuing of a current error with a current error' do
48
+ expect { raise SampleClass, nil }.to raise_error(SampleClass)
49
+ end
50
+
51
+ it 'allows rescuing of a deprecated error with a deprecated error' do
52
+ expect { raise SampleDeprecatedClass, nil }.to raise_error(SampleDeprecatedClass)
53
+ end
54
+
55
+ it 'allows rescuing of a deprecated error with a current error' do
56
+ expect { raise SampleDeprecatedClass, nil }.to raise_error(SampleClass)
57
+ end
58
+
59
+
60
+ def with_warn_squelching
61
+ stderr_catcher = StringIO.new
62
+ original_stderr = $stderr
63
+ $stderr = stderr_catcher
64
+ result = yield if block_given?
65
+ [stderr_catcher.tap(&:rewind).string, result]
66
+ ensure
67
+ $stderr = original_stderr
68
+ end
69
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::ClientError do
4
+ describe '.initialize' do
5
+ subject { described_class.new(exception, response) }
6
+ let(:response) { nil }
7
+
8
+ context 'with exception only' do
9
+ let(:exception) { RuntimeError.new('test') }
10
+
11
+ it { expect(subject.wrapped_exception).to eq(exception) }
12
+ it { expect(subject.response).to be_nil }
13
+ it { expect(subject.message).to eq(exception.message) }
14
+ it { expect(subject.backtrace).to eq(exception.backtrace) }
15
+ it { expect(subject.inspect).to eq('#<Faraday::ClientError wrapped=#<RuntimeError: test>>') }
16
+ end
17
+
18
+ context 'with response hash' do
19
+ let(:exception) { { status: 400 } }
20
+
21
+ it { expect(subject.wrapped_exception).to be_nil }
22
+ it { expect(subject.response).to eq(exception) }
23
+ it { expect(subject.message).to eq('the server responded with status 400') }
24
+ it { expect(subject.inspect).to eq('#<Faraday::ClientError response={:status=>400}>') }
25
+ end
26
+
27
+ context 'with string' do
28
+ let(:exception) { 'custom message' }
29
+
30
+ it { expect(subject.wrapped_exception).to be_nil }
31
+ it { expect(subject.response).to be_nil }
32
+ it { expect(subject.message).to eq('custom message') }
33
+ it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: custom message>>') }
34
+ end
35
+
36
+ context 'with anything else #to_s' do
37
+ let(:exception) { %w[error1 error2] }
38
+
39
+ it { expect(subject.wrapped_exception).to be_nil }
40
+ it { expect(subject.response).to be_nil }
41
+ it { expect(subject.message).to eq('["error1", "error2"]') }
42
+ it { expect(subject.inspect).to eq('#<Faraday::ClientError #<Faraday::ClientError: ["error1", "error2"]>>') }
43
+ end
44
+
45
+ context 'maintains backward-compatibility until 1.0' do
46
+ it 'does not raise an error for error-namespaced classes but prints an error message' do
47
+ error_message, error = with_warn_squelching { Faraday::Error::ClientError.new('foo') }
48
+
49
+ expect(error).to be_a Faraday::ClientError
50
+ expect(error_message).to match(
51
+ Regexp.new(
52
+ 'NOTE: Faraday::Error::ClientError.new is deprecated; '\
53
+ 'use Faraday::ClientError.new instead. It will be removed in or after version 1.0'
54
+ )
55
+ )
56
+ end
57
+
58
+ it 'does not raise an error for inherited error-namespaced classes but prints an error message' do
59
+ error_message, = with_warn_squelching { Class.new(Faraday::Error::ClientError) }
60
+
61
+ expect(error_message).to match(
62
+ Regexp.new(
63
+ 'NOTE: Inheriting Faraday::Error::ClientError is deprecated; '\
64
+ 'use Faraday::ClientError instead. It will be removed in or after version 1.0'
65
+ )
66
+ )
67
+ end
68
+
69
+ it 'allows backward-compatible class to be subclassed' do
70
+ expect {
71
+ with_warn_squelching { Class.new(Faraday::Error::ClientError) }
72
+ }.not_to raise_error
73
+ end
74
+
75
+ it 'allows rescuing of a current error with a deprecated error' do
76
+ expect { raise Faraday::ClientError, nil }.to raise_error(Faraday::Error::ClientError)
77
+ end
78
+
79
+ it 'allows rescuing of a current error with a current error' do
80
+ expect { raise Faraday::ClientError, nil }.to raise_error(Faraday::ClientError)
81
+ end
82
+
83
+ it 'allows rescuing of a deprecated error with a deprecated error' do
84
+ expect { raise Faraday::Error::ClientError, nil }.to raise_error(Faraday::Error::ClientError)
85
+ end
86
+
87
+ it 'allows rescuing of a deprecated error with a current error' do
88
+ expect { raise Faraday::Error::ClientError, nil }.to raise_error(Faraday::ClientError)
89
+ end
90
+ end
91
+
92
+ def with_warn_squelching
93
+ stderr_catcher = StringIO.new
94
+ original_stderr = $stderr
95
+ $stderr = stderr_catcher
96
+ result = yield if block_given?
97
+ [stderr_catcher.tap(&:rewind).string, result]
98
+ ensure
99
+ $stderr = original_stderr
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Faraday::Response::RaiseError do
4
+ let(:conn) do
5
+ Faraday.new do |b|
6
+ b.response :raise_error
7
+ b.adapter :test do |stub|
8
+ stub.get('ok') { [200, { 'Content-Type' => 'text/html' }, '<body></body>'] }
9
+ stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] }
10
+ stub.get('unauthorized') { [401, { 'X-Reason' => 'because' }, 'keep looking'] }
11
+ stub.get('forbidden') { [403, { 'X-Reason' => 'because' }, 'keep looking'] }
12
+ stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
13
+ stub.get('proxy-error') { [407, { 'X-Reason' => 'because' }, 'keep looking'] }
14
+ stub.get('conflict') { [409, { 'X-Reason' => 'because' }, 'keep looking'] }
15
+ stub.get('unprocessable-entity') { [422, { 'X-Reason' => 'because' }, 'keep looking'] }
16
+ stub.get('4xx') { [499, { 'X-Reason' => 'because' }, 'keep looking'] }
17
+ stub.get('nil-status') { [nil, { 'X-Reason' => 'bailout' }, 'fail'] }
18
+ stub.get('server-error') { [500, { 'X-Error' => 'bailout' }, 'fail'] }
19
+ end
20
+ end
21
+ end
22
+
23
+ it 'raises no exception for 200 responses' do
24
+ expect { conn.get('ok') }.not_to raise_error
25
+ end
26
+
27
+ it 'raises Faraday::ClientError for 400 responses' do
28
+ expect { conn.get('bad-request') }.to raise_error(Faraday::ClientError) do |ex|
29
+ expect(ex.message).to eq('the server responded with status 400')
30
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
31
+ end
32
+ end
33
+
34
+ it 'raises Faraday::ClientError for 401 responses' do
35
+ expect { conn.get('unauthorized') }.to raise_error(Faraday::ClientError) do |ex|
36
+ expect(ex.message).to eq('the server responded with status 401')
37
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
38
+ end
39
+ end
40
+
41
+ it 'raises Faraday::ClientError for 403 responses' do
42
+ expect { conn.get('forbidden') }.to raise_error(Faraday::ClientError) do |ex|
43
+ expect(ex.message).to eq('the server responded with status 403')
44
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
45
+ end
46
+ end
47
+
48
+ it 'raises Faraday::ResourceNotFound for 404 responses' do
49
+ expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
50
+ expect(ex.message).to eq('the server responded with status 404')
51
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
52
+ end
53
+ end
54
+
55
+ it 'raises Faraday::ConnectionFailed for 407 responses' do
56
+ expect { conn.get('proxy-error') }.to raise_error(Faraday::ConnectionFailed) do |ex|
57
+ expect(ex.message).to eq('407 "Proxy Authentication Required "')
58
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
59
+ end
60
+ end
61
+
62
+ it 'raises Faraday::ClientError for 409 responses' do
63
+ expect { conn.get('conflict') }.to raise_error(Faraday::ClientError) do |ex|
64
+ expect(ex.message).to eq('the server responded with status 409')
65
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
66
+ end
67
+ end
68
+
69
+ it 'raises Faraday::ClientError for 422 responses' do
70
+ expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::ClientError) do |ex|
71
+ expect(ex.message).to eq('the server responded with status 422')
72
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
73
+ end
74
+ end
75
+
76
+ it 'raises Faraday::NilStatusError for nil status in response' do
77
+ expect { conn.get('nil-status') }.to raise_error(Faraday::NilStatusError) do |ex|
78
+ expect(ex.message).to eq('http status could not be derived from the server response')
79
+ end
80
+ end
81
+
82
+ it 'raises Faraday::ClientError for other 4xx responses' do
83
+ expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
84
+ expect(ex.message).to eq('the server responded with status 499')
85
+ expect(ex.response[:headers]['X-Reason']).to eq('because')
86
+ end
87
+ end
88
+
89
+ it 'raises Faraday::ClientError for 500 responses' do
90
+ expect { conn.get('server-error') }.to raise_error(Faraday::ClientError) do |ex|
91
+ expect(ex.message).to eq('the server responded with status 500')
92
+ expect(ex.response[:headers]['X-Error']).to eq('bailout')
93
+ end
94
+ end
95
+ end