|
73 | 73 | from ._constants import (
|
74 | 74 | DEFAULT_LIMITS,
|
75 | 75 | DEFAULT_TIMEOUT,
|
| 76 | + MAX_RETRY_DELAY, |
76 | 77 | DEFAULT_MAX_RETRIES,
|
| 78 | + INITIAL_RETRY_DELAY, |
77 | 79 | RAW_RESPONSE_HEADER,
|
78 | 80 | OVERRIDE_CAST_TO_HEADER,
|
79 | 81 | )
|
@@ -590,47 +592,57 @@ def base_url(self, url: URL | str) -> None:
|
590 | 592 | def platform_headers(self) -> Dict[str, str]:
|
591 | 593 | return platform_headers(self._version)
|
592 | 594 |
|
| 595 | + def _parse_retry_after_header(self, response_headers: Optional[httpx.Headers] = None) -> float | None: |
| 596 | + """Returns a float of the number of seconds (not milliseconds) to wait after retrying, or None if unspecified. |
| 597 | +
|
| 598 | + About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After |
| 599 | + See also https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax |
| 600 | + """ |
| 601 | + if response_headers is None: |
| 602 | + return None |
| 603 | + |
| 604 | + # First, try the non-standard `retry-after-ms` header for milliseconds, |
| 605 | + # which is more precise than integer-seconds `retry-after` |
| 606 | + try: |
| 607 | + retry_ms_header = response_headers.get("retry-after-ms", None) |
| 608 | + return float(retry_ms_header) / 1000 |
| 609 | + except (TypeError, ValueError): |
| 610 | + pass |
| 611 | + |
| 612 | + # Next, try parsing `retry-after` header as seconds (allowing nonstandard floats). |
| 613 | + retry_header = response_headers.get("retry-after") |
| 614 | + try: |
| 615 | + # note: the spec indicates that this should only ever be an integer |
| 616 | + # but if someone sends a float there's no reason for us to not respect it |
| 617 | + return float(retry_header) |
| 618 | + except (TypeError, ValueError): |
| 619 | + pass |
| 620 | + |
| 621 | + # Last, try parsing `retry-after` as a date. |
| 622 | + retry_date_tuple = email.utils.parsedate_tz(retry_header) |
| 623 | + if retry_date_tuple is None: |
| 624 | + return None |
| 625 | + |
| 626 | + retry_date = email.utils.mktime_tz(retry_date_tuple) |
| 627 | + return float(retry_date - time.time()) |
| 628 | + |
593 | 629 | def _calculate_retry_timeout(
|
594 | 630 | self,
|
595 | 631 | remaining_retries: int,
|
596 | 632 | options: FinalRequestOptions,
|
597 | 633 | response_headers: Optional[httpx.Headers] = None,
|
598 | 634 | ) -> float:
|
599 | 635 | max_retries = options.get_max_retries(self.max_retries)
|
600 |
| - try: |
601 |
| - # About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After |
602 |
| - # |
603 |
| - # <http-date>". See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax for |
604 |
| - # details. |
605 |
| - if response_headers is not None: |
606 |
| - retry_header = response_headers.get("retry-after") |
607 |
| - try: |
608 |
| - # note: the spec indicates that this should only ever be an integer |
609 |
| - # but if someone sends a float there's no reason for us to not respect it |
610 |
| - retry_after = float(retry_header) |
611 |
| - except Exception: |
612 |
| - retry_date_tuple = email.utils.parsedate_tz(retry_header) |
613 |
| - if retry_date_tuple is None: |
614 |
| - retry_after = -1 |
615 |
| - else: |
616 |
| - retry_date = email.utils.mktime_tz(retry_date_tuple) |
617 |
| - retry_after = int(retry_date - time.time()) |
618 |
| - else: |
619 |
| - retry_after = -1 |
620 |
| - |
621 |
| - except Exception: |
622 |
| - retry_after = -1 |
623 | 636 |
|
624 | 637 | # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says.
|
625 |
| - if 0 < retry_after <= 60: |
| 638 | + retry_after = self._parse_retry_after_header(response_headers) |
| 639 | + if retry_after is not None and 0 < retry_after <= 60: |
626 | 640 | return retry_after
|
627 | 641 |
|
628 |
| - initial_retry_delay = 0.5 |
629 |
| - max_retry_delay = 8.0 |
630 | 642 | nb_retries = max_retries - remaining_retries
|
631 | 643 |
|
632 | 644 | # Apply exponential backoff, but not more than the max.
|
633 |
| - sleep_seconds = min(initial_retry_delay * pow(2.0, nb_retries), max_retry_delay) |
| 645 | + sleep_seconds = min(INITIAL_RETRY_DELAY * pow(2.0, nb_retries), MAX_RETRY_DELAY) |
634 | 646 |
|
635 | 647 | # Apply some jitter, plus-or-minus half a second.
|
636 | 648 | jitter = 1 - 0.25 * random()
|
|
0 commit comments