stripe 1.45.0 → 1.46.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5ed148072cc048ea24b62fc7a64645d8714eec90
4
- data.tar.gz: 2f478d4c522c9cff1cf502328289233f0b71c8b2
3
+ metadata.gz: d883d1c0eb0486e3c51e5076178c4351ca70ee68
4
+ data.tar.gz: a992c6c4e854019538a0406dbca5382c8f99d77e
5
5
  SHA512:
6
- metadata.gz: 4035922aa5bd4d02bf82ea89c52c645cf761ebbedea8ca165b81a6a379af363b9a027911313d447c7db9e846e633774780a0e838b9771c3aaf0fa132992028e2
7
- data.tar.gz: ee89f0e1140edeead9e86733d3467468dcdf94551b35878ae438e0acd7dd70ec3a68ca66e3acf5e29d23d194f8cd59bb4641e5d0e6c2eb8efd6ebbe8dc991f1e
6
+ metadata.gz: caf72f6851481ed302b8d06d450b55cf72c260104e549c099f52bb7477ec0ec28777648f4d3e4edae0f1c96cfec8123248a645cb316325cefec4f04cab74fab8
7
+ data.tar.gz: 165a6679fea615754af43de70e04c5d33a225ad1e938222e7c13767736bcdd9955162b22f266044f20818758ec3bcae0bb366c1c641b3fe1a388ed441315264b
@@ -1,3 +1,7 @@
1
+ === 1.46.0 2016-07-07
2
+
3
+ * Allow retry when a 409 conflict is encountered
4
+
1
5
  === 1.45.0 2016-07-07
2
6
 
3
7
  * Do not send subresources when updating except when explicitly told to do so (see #433)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.45.0
1
+ 1.46.0
@@ -68,6 +68,23 @@ require 'stripe/errors/rate_limit_error'
68
68
  module Stripe
69
69
  DEFAULT_CA_BUNDLE_PATH = File.dirname(__FILE__) + '/data/ca-certificates.crt'
70
70
 
71
+ # HTTP status exceptions which we'd like to retry. Most HTTP status
72
+ # exceptions are from a Stripe API server response, so we generally _don't_
73
+ # retry because doing so would have the same result as the original request.
74
+ RETRY_HTTP_EXCEPTIONS = [
75
+ # A server may respond with a 409 to indicate that there is a concurrent
76
+ # request executing with the same idempotency key. In the case that a
77
+ # request failed due to a connection problem and the client has retried too
78
+ # early, but the server is still executing the old request, we would like
79
+ # the client to continue retrying until getting a "real" response status
80
+ # back.
81
+ RestClient::Conflict,
82
+
83
+ # Retry on timeout-related problems. This shouldn't be lumped in with HTTP
84
+ # exceptions, but with RestClient it is.
85
+ RestClient::RequestTimeout,
86
+ ].freeze
87
+
71
88
  @api_base = 'https://api.stripe.com'
72
89
  @connect_base = 'https://connect.stripe.com'
73
90
  @uploads_base = 'https://uploads.stripe.com'
@@ -194,24 +211,45 @@ module Stripe
194
211
  def self.execute_request_with_rescues(request_opts, api_base_url, retry_count = 0)
195
212
  begin
196
213
  response = execute_request(request_opts)
197
- rescue SocketError => e
198
- response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
199
- rescue NoMethodError => e
200
- # Work around RestClient bug
201
- if e.message =~ /\WRequestFailed\W/
202
- e = APIConnectionError.new('Unexpected HTTP response code')
214
+
215
+ # We rescue all exceptions from a request so that we have an easy spot to
216
+ # implement our retry logic across the board. We'll re-raise if it's a type
217
+ # of exception that we didn't expect to handle.
218
+ rescue => e
219
+ if should_retry?(e, retry_count)
220
+ retry_count = retry_count + 1
221
+ sleep sleep_time(retry_count)
222
+ retry
223
+ end
224
+
225
+ case e
226
+ when SocketError
227
+ response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
228
+
229
+ when NoMethodError
230
+ # Work around RestClient bug
231
+ if e.message =~ /\WRequestFailed\W/
232
+ e = APIConnectionError.new('Unexpected HTTP response code')
233
+ response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
234
+ else
235
+ raise
236
+ end
237
+
238
+ when RestClient::ExceptionWithResponse
239
+ if e.response
240
+ handle_api_error(e.response)
241
+ else
242
+ response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
243
+ end
244
+
245
+ when RestClient::Exception, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError
203
246
  response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
247
+
248
+ # Only handle errors when we know we can do so, and re-raise otherwise.
249
+ # This should be pretty infrequent.
204
250
  else
205
251
  raise
206
252
  end
207
- rescue RestClient::ExceptionWithResponse => e
208
- if e.response
209
- handle_api_error(e.response)
210
- else
211
- response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
212
- end
213
- rescue RestClient::Exception, Errno::ECONNREFUSED, OpenSSL::SSL::SSLError => e
214
- response = handle_restclient_error(e, request_opts, retry_count, api_base_url)
215
253
  end
216
254
 
217
255
  response
@@ -367,13 +405,6 @@ module Stripe
367
405
 
368
406
  def self.handle_restclient_error(e, request_opts, retry_count, api_base_url=nil)
369
407
 
370
- if should_retry?(e, retry_count)
371
- retry_count = retry_count + 1
372
- sleep sleep_time(retry_count)
373
- response = execute_request_with_rescues(request_opts, api_base_url, retry_count)
374
- return response
375
- end
376
-
377
408
  api_base_url = @api_base unless api_base_url
378
409
  connection_message = "Please check your internet connection and try again. " \
379
410
  "If this problem persists, you should check Stripe's service status at " \
@@ -419,7 +450,16 @@ module Stripe
419
450
 
420
451
  def self.should_retry?(e, retry_count)
421
452
  return false if retry_count >= self.max_network_retries
453
+
454
+ # Certificate validation problem: do not retry.
422
455
  return false if e.is_a?(RestClient::SSLCertificateNotVerified)
456
+
457
+ # Generally don't retry when we got a successful response back from the
458
+ # Stripe API server, but with some exceptions (for more details, see notes
459
+ # on RETRY_HTTP_EXCEPTIONS).
460
+ return false if e.is_a?(RestClient::ExceptionWithResponse) &&
461
+ !RETRY_HTTP_EXCEPTIONS.any? { |klass| e.is_a?(klass) }
462
+
423
463
  return true
424
464
  end
425
465
 
@@ -1,3 +1,3 @@
1
1
  module Stripe
2
- VERSION = '1.45.0'
2
+ VERSION = '1.46.0'
3
3
  end
@@ -685,14 +685,14 @@ module Stripe
685
685
  end
686
686
 
687
687
  context "with retries" do
688
-
689
688
  setup do
690
689
  Stripe.stubs(:max_network_retries).returns(2)
691
690
  end
692
691
 
693
692
  should 'retry failed network requests if specified and raise if error persists' do
694
693
  Stripe.expects(:sleep_time).at_least_once.returns(0)
695
- @mock.expects(:post).times(3).with('https://api.stripe.com/v1/charges', nil, 'amount=50&currency=usd').raises(Errno::ECONNREFUSED.new)
694
+ @mock.expects(:post).times(3).with('https://api.stripe.com/v1/charges', nil, 'amount=50&currency=usd').
695
+ raises(Errno::ECONNREFUSED.new)
696
696
 
697
697
  err = assert_raises Stripe::APIConnectionError do
698
698
  Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
@@ -704,34 +704,15 @@ module Stripe
704
704
  Stripe.expects(:sleep_time).at_least_once.returns(0)
705
705
  response = make_response({"id" => "myid"})
706
706
  err = Errno::ECONNREFUSED.new
707
- @mock.expects(:post).times(2).with('https://api.stripe.com/v1/charges', nil, 'amount=50&currency=usd').raises(err).then.returns(response)
707
+ @mock.expects(:post).times(2).with('https://api.stripe.com/v1/charges', nil, 'amount=50&currency=usd').
708
+ raises(Errno::ECONNREFUSED.new).
709
+ then.
710
+ returns(response)
708
711
 
709
712
  result = Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
710
713
  assert_equal "myid", result.id
711
714
  end
712
715
 
713
- # We retry the request if we receive SSL errors, since these can be caused
714
- # by transient network issues, in addition to compatibility issues between
715
- # the client and server.
716
- should 'retry failed network requests if they fail with OpenSSL::SSL::SSLError' do
717
- Stripe.expects(:sleep_time).at_least_once.returns(0)
718
- @mock.expects(:post).times(3).with('https://api.stripe.com/v1/charges', nil, 'amount=50&currency=usd').raises(OpenSSL::SSL::SSLError.new('message'))
719
-
720
- err = assert_raises Stripe::APIConnectionError do
721
- Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
722
- end
723
- assert_match(/Request was retried 2 times/, err.message)
724
- end
725
-
726
- should 'not retry a SSLCertificateNotVerified error' do
727
- @mock.expects(:post).times(1).with('https://api.stripe.com/v1/charges', nil, 'amount=50&currency=usd').raises(RestClient::SSLCertificateNotVerified.new('message'))
728
-
729
- err = assert_raises Stripe::APIConnectionError do
730
- Stripe::Charge.create(:amount => 50, :currency => 'usd', :card => { :number => nil })
731
- end
732
- assert_no_match(/retried/, err.message)
733
- end
734
-
735
716
  should 'not add an idempotency key to GET requests' do
736
717
  SecureRandom.expects(:uuid).times(0)
737
718
  Stripe.expects(:execute_request).with do |opts|
@@ -770,8 +751,33 @@ module Stripe
770
751
 
771
752
  end
772
753
 
773
- context "sleep_time" do
754
+ context ".should_retry?" do
755
+ setup do
756
+ Stripe.stubs(:max_network_retries).returns(2)
757
+ end
758
+
759
+ should 'retry on a low-level network error' do
760
+ assert Stripe.should_retry?(Errno::ECONNREFUSED.new, 0)
761
+ end
762
+
763
+ should 'retry on timeout' do
764
+ assert Stripe.should_retry?(RestClient::RequestTimeout.new, 0)
765
+ end
774
766
 
767
+ should 'retry on a conflict' do
768
+ assert Stripe.should_retry?(RestClient::Conflict.new, 0)
769
+ end
770
+
771
+ should 'not retry at maximum count' do
772
+ refute Stripe.should_retry?(RuntimeError.new, Stripe.max_network_retries)
773
+ end
774
+
775
+ should 'not retry on a certificate validation error' do
776
+ refute Stripe.should_retry?(RestClient::SSLCertificateNotVerified.new('message'), 0)
777
+ end
778
+ end
779
+
780
+ context ".sleep_time" do
775
781
  should "should grow exponentially" do
776
782
  Stripe.stubs(:rand).returns(1)
777
783
  Stripe.stubs(:max_network_retry_delay).returns(999)
@@ -808,7 +814,6 @@ module Stripe
808
814
  assert_equal(base_value * 4, Stripe.sleep_time(3))
809
815
  assert_equal(base_value * 8, Stripe.sleep_time(4))
810
816
  end
811
-
812
817
  end
813
818
  end
814
819
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stripe
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.45.0
4
+ version: 1.46.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe