From c701e6104358c0307c2fd18895c4952cc55da092 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Sun, 11 Feb 2018 22:09:36 +0100 Subject: [PATCH 2/2] retry HTTP requests 3 times (fixes #21783) Retry is applied to access token request and user info requests (through OAuth2Session). There is a small exponential backoff of 0.5 and 1s. Also decrease log level of message for failure of retrieval of the access token or the user info to the level WARNING, that's never been a problem for the user, as he was correctly redirected to its origin (usually and IdP endpoint). --- src/authentic2_auth_fc/utils.py | 25 +++++++++++++++++++++++++ src/authentic2_auth_fc/views.py | 16 +++++++++------- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/authentic2_auth_fc/utils.py b/src/authentic2_auth_fc/utils.py index f2ba2f7..98c5d7b 100644 --- a/src/authentic2_auth_fc/utils.py +++ b/src/authentic2_auth_fc/utils.py @@ -5,6 +5,10 @@ import json import datetime import uuid +import requests +from requests.adapters import HTTPAdapter +from requests.packages.urllib3.util.retry import Retry + from django.core.urlresolvers import reverse from django.conf import settings from django.shortcuts import resolve_url @@ -158,3 +162,24 @@ def apply_user_info_mappings(user, user_info): tags.add(mapping['tag']) if save_user: user.save() + + +def requests_retry_session( + retries=3, + backoff_factor=0.5, + status_forcelist=(500, 502, 504), + session=None, +): + '''Create a requests session which retries after 0.5s then 1s''' + session = session or requests.Session() + retry = Retry( + total=retries, + read=retries, + connect=retries, + backoff_factor=backoff_factor, + status_forcelist=status_forcelist, + ) + adapter = HTTPAdapter(max_retries=retry) + session.mount('http://', adapter) + session.mount('https://', adapter) + return session diff --git a/src/authentic2_auth_fc/views.py b/src/authentic2_auth_fc/views.py index 80e1afa..c79bdc2 100644 --- a/src/authentic2_auth_fc/views.py +++ b/src/authentic2_auth_fc/views.py @@ -1,9 +1,9 @@ import uuid -import requests import logging import json import urlparse import urllib +import requests from requests_oauthlib import OAuth2Session @@ -86,10 +86,11 @@ def resolve_access_token(authorization_code, redirect_uri, logger): } logger.debug('access token request %s', data) try: - response = requests.post( + session = utils.requests_retry_session() + response = session.post( app_settings.token_url, data=data, verify=app_settings.verify_certificate, - allow_redirects=False, timeout=10) + allow_redirects=False, timeout=3) if response.status_code != 200: try: data = response.json() @@ -99,7 +100,7 @@ def resolve_access_token(authorization_code, redirect_uri, logger): logger.warning(u'oauth2 error on access token retrieval: %r', response.content) return except requests.exceptions.RequestException as e: - logger.error(u'unable to retrieve access token {}'.format(e)) + logger.warning(u'unable to retrieve access token {}'.format(e)) else: try: response = response.json() @@ -193,7 +194,7 @@ class FcOAuthSessionViewMixin(LoggerMixin): data = self.oauth_session().get(url, verify=verify, allow_redirects=False, timeout=3) data.raise_for_status() except requests.exceptions.RequestException as e: - self.logger.error(u'unable to retrieve ressource from {} due to {}'.format(url, e)) + self.logger.warning(u'unable to retrieve ressource from {} due to {}'.format(url, e)) else: try: data = data.json() @@ -270,8 +271,9 @@ class FcOAuthSessionViewMixin(LoggerMixin): self.logger.debug('fc id_token %s', self.id_token) for key in self.id_token: setattr(self, key, self.id_token[key]) - self.oauth_session = lambda: OAuth2Session( - app_settings.client_id, token=self.token) + self.oauth_session = lambda: utils.requests_retry_session( + session=OAuth2Session( + app_settings.client_id, token=self.token)) self.user_info = self.get_user_info() if not self.user_info: msg = 'userinfo resolution failed : {}'.format(self.token) -- 2.14.2