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
Improve implementation of respect_retry_after_header #1607
Changes from 5 commits
7ac8cee
084ea70
ef2c9b7
4431efa
75c9b69
9591a02
01be1d1
52b82db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
from mock import patch | ||
import pytest | ||
|
||
from urllib3.response import HTTPResponse | ||
from urllib3.packages import six | ||
from urllib3.packages.six.moves import xrange | ||
from urllib3.util.retry import Retry, RequestHistory | ||
from urllib3.exceptions import ( | ||
ConnectTimeoutError, | ||
InvalidHeader, | ||
MaxRetryError, | ||
ReadTimeoutError, | ||
ResponseError, | ||
|
@@ -271,3 +274,47 @@ def test_retry_set_remove_headers_on_redirect(self): | |
retry = Retry(remove_headers_on_redirect=["X-API-Secret"]) | ||
|
||
assert list(retry.remove_headers_on_redirect) == ["x-api-secret"] | ||
|
||
@pytest.mark.parametrize("value", ["-1", "+1", "1.0", six.u("\xb2")]) # \xb2 = ^2 | ||
def test_parse_retry_after_invalid(self, value): | ||
retry = Retry() | ||
with pytest.raises(InvalidHeader): | ||
retry.parse_retry_after(value) | ||
|
||
@pytest.mark.parametrize( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add a test case for an http-date formatted Retry-After value? May require mocking time.time() to get a consistent result. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added four cases here (whether the time is in the future or not X whether we're respecting the header or not). |
||
"value, expected", [("0", 0), ("1000", 1000), ("\t42 ", 42)] | ||
) | ||
def test_parse_retry_after(self, value, expected): | ||
retry = Retry() | ||
assert retry.parse_retry_after(value) == expected | ||
|
||
@pytest.mark.parametrize("respect_retry_after_header", [True, False]) | ||
def test_respect_retry_after_header_propagated(self, respect_retry_after_header): | ||
|
||
retry = Retry(respect_retry_after_header=respect_retry_after_header) | ||
new_retry = retry.new() | ||
assert new_retry.respect_retry_after_header == respect_retry_after_header | ||
|
||
@pytest.mark.parametrize( | ||
"retry_after_header,respect_retry_after_header,sleep_duration", | ||
[("3600", True, 3600), ("3600", False, None)], | ||
) | ||
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) | ||
|
||
with 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} | ||
) | ||
|
||
retry.sleep(response) | ||
|
||
# The expected behavior is that we'll only sleep if respecting | ||
# this header (since we won't have any backoff sleep attempts) | ||
if respect_retry_after_header: | ||
sleep_mock.assert_called_with(sleep_duration) | ||
else: | ||
sleep_mock.assert_not_called() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we add a new unit test that hits this condition?