Use tz-aware functions in Retry.parse_retry_after()
MODIFY
dev-requirements.txt
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

MODIFY
src/urllib3/util/retry.py
src/urllib3/util/retry.py
@@ -266,10 +266,10 @@ class Retry(object):
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:

MODIFY
test/test_retry.py
test/test_retry.py
-import datetime
import mock
import pytest
-import time
from urllib3.response import HTTPResponse
from urllib3.packages import six
@@ -313,6 +311,7 @@ class TestRetry(object):
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 @@ class TestRetry(object):
("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 @@ class TestRetry(object):
):
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}

Use tz-aware functions in Retry.parse_retry_after()