diff --git a/passerelle/utils/__init__.py b/passerelle/utils/__init__.py
index a9942ad3..e067b6da 100644
--- a/passerelle/utils/__init__.py
+++ b/passerelle/utils/__init__.py
@@ -254,6 +254,15 @@ def log_http_request(
# - use a proxy for HTTP and HTTPS if resource.http_proxy exists
+def to_registry_key(value):
+ if isinstance(value, dict):
+ return tuple((key, to_registry_key(value[key])) for key in sorted(value.keys()))
+ elif isinstance(value, list):
+ return tuple(to_registry_key(x) for x in value)
+ else:
+ return value
+
+
class Request(RequestSession):
ADAPTER_REGISTRY = {} # connection pooling
log_requests_errors = True
@@ -262,7 +271,10 @@ class Request(RequestSession):
self.logger = kwargs.pop('logger')
self.resource = kwargs.pop('resource', None)
resource_log_requests_errors = getattr(self.resource, 'log_requests_errors', True)
- self.log_requests_errors = kwargs.pop('log_requests_errors', resource_log_requests_errors)
+ resource_is_down = self.resource.down() if hasattr(self.resource, 'down') else False
+ self.log_requests_errors = kwargs.pop(
+ 'log_requests_errors', resource_log_requests_errors and not resource_is_down
+ )
timeout = kwargs.pop('timeout', None)
super().__init__(*args, **kwargs)
if self.resource:
@@ -271,11 +283,12 @@ class Request(RequestSession):
requests_max_retries = dict(settings.REQUESTS_MAX_RETRIES)
if getattr(self.resource, 'requests_max_retries', None):
requests_max_retries = dict(self.resource.requests_max_retries)
- if requests_max_retries:
+ if requests_max_retries and not resource_is_down:
requests_max_retries.setdefault('read', None)
http_adapter_init_kwargs['max_retries'] = Retry(**requests_max_retries)
adapter = Request.ADAPTER_REGISTRY.setdefault(
- type(self.resource), HTTPAdapter(**http_adapter_init_kwargs)
+ (type(self.resource), to_registry_key(http_adapter_init_kwargs)),
+ HTTPAdapter(**http_adapter_init_kwargs),
)
self.mount('https://', adapter)
self.mount('http://', adapter)
diff --git a/tests/test_requests.py b/tests/test_requests.py
index 4620ab5c..70c5c22f 100644
--- a/tests/test_requests.py
+++ b/tests/test_requests.py
@@ -7,7 +7,8 @@ import requests
import responses
from django.test import override_settings
from httmock import HTTMock, response, urlmatch
-from urllib3.exceptions import ReadTimeoutError
+from responses.registries import OrderedRegistry
+from urllib3.exceptions import ConnectionError, ReadTimeoutError
from passerelle.utils import CaseInsensitiveDict, Request, log_http_request
from passerelle.utils.http_authenticators import HawkAuth
@@ -665,3 +666,74 @@ def test_requests_substitution(settings):
requests.get('https://example.com/html?bar=foo', params={'foo': 'bar'}).text
== '\n\n'
)
+
+
+@responses.activate(registry=OrderedRegistry) # pylint: disable=unexpected-keyword-arg,no-value-for-parameter
+def test_requests_resource_down():
+ from passerelle.base.models import BaseResource
+
+ resource = mock.Mock()
+ resource.requests_max_retries = {}
+ resource.slug = 'test'
+ resource.get_connector_slug.return_value = 'cmis'
+ resource.get_settings = lambda: BaseResource.get_settings(resource)
+ resource.get_setting = lambda name: BaseResource.get_setting(resource, name)
+ resource.down = mock.Mock(return_value=False)
+
+ logger = mock.Mock()
+ requests = Request(resource=resource, logger=logger)
+
+ responses.add(
+ responses.GET,
+ "https://example.com/exception",
+ body=ConnectionError('down'),
+ )
+ with pytest.raises(ConnectionError):
+ requests.get('https://example.com/exception')
+ assert logger.error.call_count == 1
+ assert logger.info.call_count == 0
+
+ responses.add(
+ responses.GET,
+ "https://example.com/exception",
+ body=ConnectionError('down'),
+ )
+ logger = mock.Mock()
+ resource.down.return_value = True
+ requests = Request(resource=resource, logger=logger)
+ with pytest.raises(ConnectionError):
+ requests.get('https://example.com/exception')
+ assert logger.error.call_count == 0
+ assert logger.info.call_count == 1
+
+ responses.add(
+ responses.GET,
+ "https://example.com/exception",
+ body='Error',
+ status=500,
+ )
+ responses.add(
+ responses.GET,
+ "https://example.com/exception",
+ body='ok',
+ )
+ resource.down.return_value = False
+ resource.requests_max_retries = {
+ 'total': 3,
+ 'backoff_factor': 0.001,
+ 'status_forcelist': [500],
+ 'connect': 1,
+ 'read': 1,
+ }
+ requests = Request(resource=resource, logger=logger)
+ assert requests.get('https://example.com/exception').text == 'ok'
+
+ responses.add(
+ responses.GET,
+ "https://example.com/exception",
+ body='Error',
+ status=500,
+ )
+ resource.down.return_value = True
+ requests = Request(resource=resource, logger=logger)
+ assert requests.get('https://example.com/exception').status_code == 500