diff --git a/src/api/views.py b/src/api/views.py index 85ae021c9..b3c371494 100644 --- a/src/api/views.py +++ b/src/api/views.py @@ -6,6 +6,8 @@ from django.utils.safestring import mark_safe from registrar.templatetags.url_helpers import public_site_url from registrar.utility.errors import GenericError, GenericErrorCodes +# comment out after testing +from epplibwrapper.errors import RegistryError import requests @@ -67,6 +69,9 @@ def check_domain_available(domain): a match. If check fails, throws a RegistryError. """ Domain = apps.get_model("registrar.Domain") + # TODO: remove this block it is used for testing on dev sandbox to verify error retry + if "bad" in domain: + raise RegistryError("Forced Registry Error from bad domain") if domain.endswith(".gov"): return Domain.available(domain) else: diff --git a/src/epplibwrapper/client.py b/src/epplibwrapper/client.py index cfb41f4ea..5693b6993 100644 --- a/src/epplibwrapper/client.py +++ b/src/epplibwrapper/client.py @@ -17,7 +17,7 @@ except ImportError: from django.conf import settings from .cert import Cert, Key -from .errors import LoginError, RegistryError +from .errors import ErrorCode, LoginError, RegistryError from .socket import Socket from .utility.pool import EPPConnectionPool @@ -115,7 +115,7 @@ class EPPLibWrapper: except TransportError as err: message = f"{cmd_type} failed to execute due to a connection error." logger.error(f"{message} Error: {err}", exc_info=True) - raise RegistryError(message) from err + raise RegistryError(message, code=ErrorCode.TRANSPORT_ERROR) from err except LoginError as err: # For linter due to it not liking this line length text = "failed to execute due to a registry login error." @@ -159,11 +159,15 @@ class EPPLibWrapper: self.start_connection_pool() counter = 0 # we'll try 3 times + logger.info("Counter set to: ", counter) while True: try: + logger.info("Trying self._send(command)") return self._send(command) except RegistryError as err: - if err.should_retry() and counter < 3: + logger.info("Exception found") + if counter < 3 and (err.should_retry() or err.is_transport_error()): + logger.info("Retrying transport error. Attempt #", counter) counter += 1 sleep((counter * 50) / 1000) # sleep 50 ms to 150 ms else: # don't try again diff --git a/src/epplibwrapper/errors.py b/src/epplibwrapper/errors.py index d34ed5e91..2b7bdd255 100644 --- a/src/epplibwrapper/errors.py +++ b/src/epplibwrapper/errors.py @@ -4,13 +4,15 @@ from enum import IntEnum class ErrorCode(IntEnum): """ Overview of registry response codes from RFC 5730. See RFC 5730 for full text. - + - 0 System connection error - 1000 - 1500 Success - 2000 - 2308 Registrar did something silly - 2400 - 2500 Registry did something silly - 2501 - 2502 Something malicious or abusive may have occurred """ + TRANSPORT_ERROR = 0 + COMMAND_COMPLETED_SUCCESSFULLY = 1000 COMMAND_COMPLETED_SUCCESSFULLY_ACTION_PENDING = 1001 COMMAND_COMPLETED_SUCCESSFULLY_NO_MESSAGES = 1300 @@ -67,6 +69,9 @@ class RegistryError(Exception): def should_retry(self): return self.code == ErrorCode.COMMAND_FAILED + def is_transport_error(self): + return self.code == ErrorCode.TRANSPORT_ERROR + # connection errors have error code of None and [Errno 99] in the err message def is_connection_error(self): return self.code is None diff --git a/src/epplibwrapper/tests/test_pool.py b/src/epplibwrapper/tests/test_pool.py index 1c36d26da..ab4323ff3 100644 --- a/src/epplibwrapper/tests/test_pool.py +++ b/src/epplibwrapper/tests/test_pool.py @@ -246,3 +246,7 @@ class TestConnectionPool(TestCase): expected = "InfoDomain failed to execute due to a connection error." result = registry.send(commands.InfoDomain(name="test.gov"), cleaned=True) self.assertEqual(result, expected) + + @patch.object(EPPLibWrapper, "_test_registry_connection_success", patch_success) + def test_retries_on_transport_error(self): + """A .send is invoked on the pool, but [description for transport error]."""