Skip to content

Commit

Permalink
Merge pull request #3464 from candlepin/ptoscano/retry-after
Browse files Browse the repository at this point in the history
fix: Handle Retry-After headers better for 429 responses
  • Loading branch information
jirihnidek authored Oct 14, 2024
2 parents 5e094e3 + 4c8a44a commit b568393
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 5 deletions.
8 changes: 6 additions & 2 deletions src/rhsm/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,14 @@ class RateLimitExceededException(RestlibException):
The retry_after attribute may not be included in the response.
"""

def __init__(self, code: int, msg: str = None, headers: str = None) -> None:
def __init__(self, code: int, msg: str = None, headers: dict = None) -> None:
super(RateLimitExceededException, self).__init__(code, msg)
self.headers = headers or {}
self.retry_after = safe_int(self.headers.get("retry-after"))
self.retry_after = None
for header, value in self.headers.items():
if header.lower() == "retry-after":
self.retry_after = safe_int(value)
break
self.msg = msg or "Access rate limit exceeded"
if self.retry_after is not None:
self.msg += ", retry access after: %s seconds." % self.retry_after
Expand Down
11 changes: 8 additions & 3 deletions src/subscription_manager/scripts/rhsmcertd_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,15 +288,20 @@ def _auto_register_anonymous(uep: "UEPConnection", token: Dict[str, str]) -> Non
log.debug(report)
return
except connection.RateLimitExceededException as exc:
if exc.headers.get("Retry-After", None) is None:
if exc.retry_after is None:
log.warning(
"Server did not include Retry-After header in rate-limited response. "
f"headers={exc.headers}"
)
raise
delay = int(exc.headers["Retry-After"])
delay = exc.retry_after
log.debug(
f"Got response with status code {exc.code} and Retry-After header, "
f"will try again in {delay} seconds."
)
time.sleep(delay)
except Exception:
except Exception as exc:
log.warning(f"Anonymous registration failed, server returned {exc}.")
raise

# In theory, this should not happen, it means that something has gone wrong server-side.
Expand Down
12 changes: 12 additions & 0 deletions test/rhsm/unit/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,18 @@ def test_429_body(self):
else:
self.fail("Should have raised a RateLimitExceededException")

def test_429_weird_case(self):
content = '{"errors": ["TooFast"]}'
headers = {"RETry-aFteR": 20}
try:
self.vr("429", content, headers)
except RateLimitExceededException as e:
self.assertEqual(20, e.retry_after)
self.assertEqual("TooFast, retry access after: 20 seconds.", e.msg)
self.assertEqual("429", e.code)
else:
self.fail("Should have raised a RateLimitExceededException")

def test_500_empty(self):
try:
self.vr("500", "")
Expand Down

0 comments on commit b568393

Please sign in to comment.