Merge pull request #4114 from certbot/auto-retry-0.10.x

Automatically retry after getting badNonce error part 2
This commit is contained in:
Brad Warren 2017-01-25 18:52:52 -08:00 committed by GitHub
commit 2ca44a064b
2 changed files with 42 additions and 4 deletions

View file

@ -658,8 +658,25 @@ class ClientNetwork(object): # pylint: disable=too-many-instance-attributes
self._add_nonce(self.head(url))
return self._nonces.pop()
def post(self, url, obj, content_type=JOSE_CONTENT_TYPE, **kwargs):
"""POST object wrapped in `.JWS` and check response."""
def post(self, *args, **kwargs):
"""POST object wrapped in `.JWS` and check response.
If the server responded with a badNonce error, the request will
be retried once.
"""
should_retry = True
while True:
try:
return self._post_once(*args, **kwargs)
except messages.Error as error:
if should_retry and error.code == 'badNonce':
logger.debug('Retrying request after error:\n%s', error)
should_retry = False
else:
raise
def _post_once(self, url, obj, content_type=JOSE_CONTENT_TYPE, **kwargs):
data = self._wrap_in_jws(obj, self._get_nonce(url))
kwargs.setdefault('headers', {'Content-Type': content_type})
response = self._send_request('POST', url, data=data, **kwargs)

View file

@ -616,7 +616,9 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
self.wrapped_obj = mock.MagicMock()
self.content_type = mock.sentinel.content_type
self.all_nonces = [jose.b64encode(b'Nonce'), jose.b64encode(b'Nonce2')]
self.all_nonces = [
jose.b64encode(b'Nonce'),
jose.b64encode(b'Nonce2'), jose.b64encode(b'Nonce3')]
self.available_nonces = self.all_nonces[:]
def send_request(*args, **kwargs):
@ -664,7 +666,7 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
self.net._wrap_in_jws.assert_called_once_with(
self.obj, jose.b64decode(self.all_nonces.pop()))
assert not self.available_nonces
self.available_nonces = []
self.assertRaises(errors.MissingNonce, self.net.post,
'uri', self.obj, content_type=self.content_type)
self.net._wrap_in_jws.assert_called_with(
@ -680,6 +682,25 @@ class ClientNetworkWithMockedResponseTest(unittest.TestCase):
self.assertRaises(errors.BadNonce, self.net.post, 'uri',
self.obj, content_type=self.content_type)
def test_post_failed_retry(self):
check_response = mock.MagicMock()
check_response.side_effect = messages.Error.with_code('badNonce')
# pylint: disable=protected-access
self.net._check_response = check_response
self.assertRaises(messages.Error, self.net.post, 'uri',
self.obj, content_type=self.content_type)
def test_post_successful_retry(self):
check_response = mock.MagicMock()
check_response.side_effect = [messages.Error.with_code('badNonce'),
self.checked_response]
# pylint: disable=protected-access
self.net._check_response = check_response
self.assertEqual(self.checked_response, self.net.post(
'uri', self.obj, content_type=self.content_type))
def test_head_get_post_error_passthrough(self):
self.send_request.side_effect = requests.exceptions.RequestException
for method in self.net.head, self.net.get: