Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use tz-aware functions in Retry.parse_retry_after() #1932

Merged
merged 1 commit into from Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions dev-requirements.txt
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/urllib3/util/retry.py
Expand Up @@ -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:
Expand Down
20 changes: 7 additions & 13 deletions 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
Expand Down Expand Up @@ -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",
[
Expand All @@ -326,24 +325,19 @@ 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(
self, retry_after_header, respect_retry_after_header, sleep_duration
):
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}
Expand Down