summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.opendev.org>2023-03-21 19:37:06 +0000
committerGerrit Code Review <review@openstack.org>2023-03-21 19:37:06 +0000
commit62641a2f49e0d9e66730e0c5932c4350cad44cc4 (patch)
tree83d1120469fdcea0c9982ce6877d093b9269c564
parent9e703b9345cffd90d723e0ce5c180a385c75c62e (diff)
parent099dd66265dfdc94e162eba4c1ec3a33b305bc26 (diff)
downloadpython-swiftclient-62641a2f49e0d9e66730e0c5932c4350cad44cc4.tar.gz
Merge "Retry on SSLError"
-rw-r--r--swiftclient/client.py9
-rw-r--r--test/unit/test_swiftclient.py58
2 files changed, 64 insertions, 3 deletions
diff --git a/swiftclient/client.py b/swiftclient/client.py
index b9f12aa..0635090 100644
--- a/swiftclient/client.py
+++ b/swiftclient/client.py
@@ -1798,8 +1798,13 @@ class Connection:
service_token=self.service_token, **kwargs)
self._add_response_dict(caller_response_dict, kwargs)
return rv
- except SSLError:
- raise
+ except SSLError as e:
+ self._add_response_dict(caller_response_dict, kwargs)
+ if ('certificate verify' in str(e)) or \
+ ('hostname' in str(e)) or \
+ self.attempts > self.retries:
+ raise
+ self.http_conn = None
except (socket.error, RequestException):
self._add_response_dict(caller_response_dict, kwargs)
if self.attempts > self.retries:
diff --git a/test/unit/test_swiftclient.py b/test/unit/test_swiftclient.py
index 55b4679..42d470c 100644
--- a/test/unit/test_swiftclient.py
+++ b/test/unit/test_swiftclient.py
@@ -25,7 +25,7 @@ import warnings
import tempfile
from hashlib import md5
from urllib.parse import urlparse
-from requests.exceptions import RequestException
+from requests.exceptions import RequestException, SSLError
from .utils import (MockHttpTest, fake_get_auth_keystone, StubResponse,
FakeKeystone)
@@ -2176,6 +2176,62 @@ class TestConnection(MockHttpTest):
self.assertEqual(mock_auth.call_count, 1)
self.assertEqual(conn.attempts, conn.retries + 1)
+ def test_no_retry_with_cert_sslerror(self):
+ def quick_sleep(*args):
+ pass
+ c.sleep = quick_sleep
+ for err in (
+ # Taken from real testing (requests==2.25.1, urllib3==1.26.5,
+ # pyOpenSSL==21.0.0) but note that these are actually way more
+ # messy/wrapped up in other exceptions
+ SSLError(
+ '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
+ 'certificate has expired (_ssl.c:997)'),
+ SSLError(
+ "hostname 'wrong.host.badssl.com' doesn't match either of "
+ "'*.badssl.com', 'badssl.com'"),
+ SSLError(
+ '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
+ 'self-signed certificate (_ssl.c:997)'),
+ SSLError(
+ '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
+ 'self-signed certificate in certificate chain (_ssl.c:997)'),
+ SSLError(
+ '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
+ 'unable to get local issuer certificate (_ssl.c:997)'),
+ SSLError(
+ '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: '
+ 'CA signature digest algorithm too weak (_ssl.c:997)'),
+ ):
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
+ with self.subTest(err=err), mock.patch(
+ 'swiftclient.client.http_connection') as \
+ fake_http_connection, \
+ mock.patch('swiftclient.client.get_auth_1_0') as mock_auth:
+ mock_auth.return_value = ('http://mock.com', 'mock_token')
+ fake_http_connection.side_effect = err
+ self.assertRaises(socket.error, conn.head_account)
+ self.assertEqual(mock_auth.call_count, 1)
+ self.assertEqual(conn.attempts, 1)
+
+ def test_retry_with_non_cert_sslerror(self):
+ def quick_sleep(*args):
+ pass
+ c.sleep = quick_sleep
+ conn = c.Connection('http://www.test.com', 'asdf', 'asdf')
+ with mock.patch('swiftclient.client.http_connection') as \
+ fake_http_connection, \
+ mock.patch('swiftclient.client.get_auth_1_0') as mock_auth:
+ mock_auth.return_value = ('http://mock.com', 'mock_token')
+ fake_http_connection.side_effect = SSLError(
+ "HTTPSConnectionPool(host='example.com', port=443): "
+ "Max retries exceeded with url: /v1/AUTH_test (Caused by "
+ "SSLError(SSLZeroReturnError(6, 'TLS/SSL connection has "
+ "been closed (EOF) (_ssl.c:997)')))")
+ self.assertRaises(socket.error, conn.head_account)
+ self.assertEqual(mock_auth.call_count, 1)
+ self.assertEqual(conn.attempts, conn.retries + 1)
+
def test_retry_with_force_auth_retry_exceptions(self):
def quick_sleep(*args):
pass