faraday 0.17.0 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
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