Skip to content

Commit 75ab630

Browse files
authored
Set global request timeouts + disable retries (#37)
1 parent 8c463ea commit 75ab630

File tree

3 files changed

+31
-15
lines changed

3 files changed

+31
-15
lines changed

flask_multipass_cern.py

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,31 @@
2828
CERN_OIDC_WELLKNOWN_URL = 'https://auth.cern.ch/auth/realms/cern/.well-known/openid-configuration'
2929
# not sure if retries are still needed, but by not using a backoff we don't risk taking down the site
3030
# using this library in case the API is persistently failing with an error
31-
HTTP_RETRY_COUNT = 2
32-
retry_config = HTTPAdapter(max_retries=Retry(total=HTTP_RETRY_COUNT,
33-
backoff_factor=0,
34-
status_forcelist=[503, 504],
35-
allowed_methods=frozenset(['GET']),
36-
raise_on_status=False))
31+
HTTP_RETRY_COUNT = 0
32+
retry_config = Retry(
33+
total=HTTP_RETRY_COUNT,
34+
backoff_factor=0,
35+
status_forcelist=[503, 504],
36+
allowed_methods=frozenset(['GET']),
37+
raise_on_status=False,
38+
)
3739
_cache_miss = object()
3840

3941

42+
class TimeoutHTTPAdapter(HTTPAdapter):
43+
def __init__(self, *args, timeout=None, **kwargs):
44+
self._default_timeout = timeout
45+
super().__init__(*args, **kwargs)
46+
47+
def send(self, request, *, timeout=None, **kwargs):
48+
if timeout is None:
49+
timeout = self._default_timeout
50+
return super().send(request, timeout=timeout, **kwargs)
51+
52+
53+
http_adapter = TimeoutHTTPAdapter(timeout=10, max_retries=retry_config)
54+
55+
4056
class ExtendedCache:
4157
def __init__(self, cache):
4258
self.cache = self._init_cache(cache)
@@ -393,7 +409,7 @@ def search_identities_ex(self, criteria, exact=False, limit=None):
393409
results = []
394410
total = 0
395411
try:
396-
results, total = self._fetch_all(api_session, '/api/v1.0/Identity', params, limit=limit)
412+
results, total = self._fetch_all(api_session, '/api/v1.0/Identity', params, limit=limit, timeout=20)
397413
except RequestException:
398414
self.logger.warning('Refreshing identities failed for criteria %s', criteria)
399415
if use_cache and cached_data:
@@ -483,7 +499,7 @@ def _get_api_session(self):
483499
token = self.cache.get(cache_key)
484500
if token:
485501
oauth_session = OAuth2Session(token=token, leeway=0)
486-
oauth_session.mount(self.authz_api_base, retry_config)
502+
oauth_session.mount(self.authz_api_base, http_adapter)
487503
return oauth_session
488504
meta = self.authlib_client.load_server_metadata()
489505
token_endpoint = meta['token_endpoint'].replace('protocol/openid-connect', 'api-access')
@@ -494,7 +510,7 @@ def _get_api_session(self):
494510
grant_type='client_credentials',
495511
leeway=0, # we handle leeway ourselves via the cache duration
496512
)
497-
oauth_session.mount(self.authz_api_base, retry_config)
513+
oauth_session.mount(self.authz_api_base, http_adapter)
498514
oauth_session.fetch_access_token(
499515
audience='authorization-service-api',
500516
headers={'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'},
@@ -522,9 +538,9 @@ def _fetch_identity_data(self, auth_info):
522538
del data['id'] # id is always included
523539
return data
524540

525-
def _fetch_all(self, api_session, endpoint, params, limit=None, *, empty_if_404=False):
541+
def _fetch_all(self, api_session, endpoint, params, limit=None, *, empty_if_404=False, timeout=None):
526542
results = []
527-
resp = api_session.get(self.authz_api_base + endpoint, params=params)
543+
resp = api_session.get(self.authz_api_base + endpoint, params=params, timeout=timeout)
528544
if empty_if_404 and resp.status_code == 404:
529545
return [], 0
530546
resp.raise_for_status()
@@ -535,7 +551,7 @@ def _fetch_all(self, api_session, endpoint, params, limit=None, *, empty_if_404=
535551
results += data['data']
536552
if not data['pagination']['next'] or (limit is not None and len(results) >= limit):
537553
break
538-
resp = api_session.get(self.authz_api_base + data['pagination']['next'])
554+
resp = api_session.get(self.authz_api_base + data['pagination']['next'], timeout=timeout)
539555
# XXX we do not expect 404s here after the first one succeeded, so here we consider it a real error
540556
resp.raise_for_status()
541557
data = resp.json()

tests/conftest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from flask_multipass import Multipass
88
from requests.sessions import Session
99

10-
from flask_multipass_cern import CERNIdentityProvider, retry_config
10+
from flask_multipass_cern import CERNIdentityProvider, http_adapter
1111

1212

1313
class MemoryCacheEntry:
@@ -49,7 +49,7 @@ def httpretty_enabled():
4949
def mock_get_api_session(mocker):
5050
mock_session = mocker.patch('flask_multipass_cern.CERNIdentityProvider._get_api_session')
5151
mock_session.return_value = Session()
52-
mock_session.return_value.mount('https://authorization-service-api.web.cern.ch', retry_config)
52+
mock_session.return_value.mount('https://authorization-service-api.web.cern.ch', http_adapter)
5353
return mock_session
5454

5555

tests/test_request_retry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
@pytest.fixture(autouse=True)
99
def faster_retries(monkeypatch):
10-
monkeypatch.setattr('flask_multipass_cern.retry_config.max_retries.backoff_factor', 0)
10+
monkeypatch.setattr('flask_multipass_cern.http_adapter.max_retries.backoff_factor', 0)
1111

1212

1313
@pytest.mark.usefixtures('httpretty_enabled', 'mock_get_api_session')

0 commit comments

Comments
 (0)