From 9ff9bf707e7281486e1cf3781db62f01b4fd8b2d Mon Sep 17 00:00:00 2001 From: Seth Michael Larson Date: Sun, 23 Aug 2020 17:07:39 -0500 Subject: [PATCH] Use tz-aware functions in Retry.parse_retry_after() --- dev-requirements.txt | 1 + src/urllib3/util/retry.py | 4 ++-- test/test_retry.py | 20 +++++++------------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index e6b480eec3..dac6785e07 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -7,6 +7,7 @@ PySocks==1.7.1 win-inet-pton==1.1.0 pytest==4.6.9 pytest-timeout==1.3.4 +pytest-freezegun==0.3.0.post1 flaky==3.6.1 trustme==0.5.3 cryptography==2.8 diff --git a/src/urllib3/util/retry.py b/src/urllib3/util/retry.py index 3d82832418..3b84a65c2d 100644 --- a/src/urllib3/util/retry.py +++ b/src/urllib3/util/retry.py @@ -266,10 +266,10 @@ def parse_retry_after(self, retry_after): if re.match(r"^\s*[0-9]+\s*$", retry_after): seconds = int(retry_after) else: - retry_date_tuple = email.utils.parsedate(retry_after) + retry_date_tuple = email.utils.parsedate_tz(retry_after) if retry_date_tuple is None: raise InvalidHeader("Invalid Retry-After header: %s" % retry_after) - retry_date = time.mktime(retry_date_tuple) + retry_date = email.utils.mktime_tz(retry_date_tuple) seconds = retry_date - time.time() if seconds < 0: diff --git a/test/test_retry.py b/test/test_retry.py index aa9a7522b2..0aee731fc5 100644 --- a/test/test_retry.py +++ b/test/test_retry.py @@ -1,7 +1,5 @@ -import datetime import mock import pytest -import time from urllib3.response import HTTPResponse from urllib3.packages import six @@ -313,6 +311,7 @@ def test_respect_retry_after_header_propagated(self, respect_retry_after_header) new_retry = retry.new() assert new_retry.respect_retry_after_header == respect_retry_after_header + @pytest.mark.freeze_time("2019-06-03 11:00:00", tz_offset=0) @pytest.mark.parametrize( "retry_after_header,respect_retry_after_header,sleep_duration", [ @@ -326,6 +325,11 @@ def test_respect_retry_after_header_propagated(self, respect_retry_after_header) ("Mon, 3 Jun 2019 11:00:00 UTC", True, None), # Won't sleep due to current time reached + not respecting header ("Mon, 3 Jun 2019 11:00:00 UTC", False, None), + # Handle all the formats in RFC 7231 Section 7.1.1.1 + ("Mon, 03 Jun 2019 11:30:12 GMT", True, 1812), + ("Monday, 03-Jun-19 11:30:12 GMT", True, 1812), + # Assume that datetimes without a timezone are in UTC per RFC 7231 + ("Mon Jun 3 11:30:12 2019", True, 1812), ], ) def test_respect_retry_after_header_sleep( @@ -333,17 +337,7 @@ def test_respect_retry_after_header_sleep( ): retry = Retry(respect_retry_after_header=respect_retry_after_header) - # Date header syntax can specify an absolute date; compare this to the - # time in the parametrized inputs above. - current_time = mock.MagicMock( - return_value=time.mktime( - datetime.datetime(year=2019, month=6, day=3, hour=11).timetuple() - ) - ) - - with mock.patch("time.sleep") as sleep_mock, mock.patch( - "time.time", current_time - ): + with mock.patch("time.sleep") as sleep_mock: # for the default behavior, it must be in RETRY_AFTER_STATUS_CODES response = HTTPResponse( status=503, headers={"Retry-After": retry_after_header}