From df383ee6e408f3be4bc3beb59aa33abc8e90f268 Mon Sep 17 00:00:00 2001 From: Peter Eckersley Date: Thu, 11 Feb 2016 15:40:31 -0800 Subject: [PATCH] Remove werkzeug dependency by parsing Retry-After ourselves Fixes #2409 Progress on #1301 --- acme/acme/client.py | 27 ++++++++++++++++++++------- acme/setup.py | 1 - 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/acme/acme/client.py b/acme/acme/client.py index 478536ecc..7c366df90 100644 --- a/acme/acme/client.py +++ b/acme/acme/client.py @@ -11,13 +11,16 @@ from six.moves import http_client # pylint: disable=import-error import OpenSSL import requests import sys -import werkzeug from acme import errors from acme import jose from acme import jws from acme import messages +try: + from email.utils import parsedate_tz +except ImportError: # pragma: no cover + from email.Utils import parsedate_tz logger = logging.getLogger(__name__) @@ -245,7 +248,8 @@ class Client(object): # pylint: disable=too-many-instance-attributes @classmethod def retry_after(cls, response, default): - """Compute next `poll` time based on response ``Retry-After`` header. + """Compute next `poll` time based on response ``Retry-After`` header, + per https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.37 :param requests.Response response: Response from `poll`. :param int default: Default value (in seconds), used when @@ -256,17 +260,26 @@ class Client(object): # pylint: disable=too-many-instance-attributes """ retry_after = response.headers.get('Retry-After', str(default)) + now = datetime.datetime.now() + year_now = now[0] try: seconds = int(retry_after) except ValueError: # pylint: disable=no-member - decoded = werkzeug.parse_date(retry_after) # RFC1123 - if decoded is None: + t = parsedate_tz(value.strip()) + try: + year = t[0] # raises TypeError if t is None + # Handle two-digit years -- but any webserver that thinks + # "retry after 99" means "come back after 1999" is.. deprecated + if year >= 0 and year < 100: + year += 2000 + t_corrected = datetime(*([year] + t[1:7])) # raises ValueError + tz = t[-1] if t[-1] else 0 + return t_corrected - timedelta(tz) # raises OverflowError + except (TypeError, ValueError, OverflowError): seconds = default - else: - return decoded - return datetime.datetime.now() + datetime.timedelta(seconds=seconds) + return now + datetime.timedelta(seconds=seconds) def poll(self, authzr): """Poll Authorization Resource for status. diff --git a/acme/setup.py b/acme/setup.py index 8b7b040e5..2daf8ad25 100644 --- a/acme/setup.py +++ b/acme/setup.py @@ -20,7 +20,6 @@ install_requires = [ 'requests', 'setuptools', # pkg_resources 'six', - 'werkzeug', ] # env markers in extras_require cause problems with older pip: #517