@@ -23,7 +23,9 @@ class RetryMiddleware(BaseMiddleware):
2323
2424 def __init__ (self , retry_configs = {}):
2525 super ().__init__ ()
26- self .total_retries : int = retry_configs .pop ('retry_total' , self .DEFAULT_TOTAL_RETRIES )
26+ self .total_retries : int = min (
27+ retry_configs .pop ('retry_total' , self .DEFAULT_TOTAL_RETRIES ), self .MAX_TOTAL_RETRIES
28+ )
2729 self .backoff_factor : float = retry_configs .pop (
2830 'retry_backoff_factor' , self .DEFAULT_BACKOFF_FACTOR
2931 )
@@ -33,7 +35,9 @@ def __init__(self, retry_configs={}):
3335 status_codes : [int ] = retry_configs .pop ('retry_on_status_codes' , [])
3436
3537 self ._retry_on_status_codes = set (status_codes ) | self ._DEFAULT_RETRY_CODES
36- self ._allowed_methods = frozenset (['HEAD' , 'GET' , 'PUT' , 'DELETE' , 'OPTIONS' , 'TRACE' ])
38+ self ._allowed_methods = frozenset (
39+ ['HEAD' , 'GET' , 'POST' , 'PUT' , 'PATCH' , 'DELETE' , 'OPTIONS' ]
40+ )
3741 self ._respect_retry_after_header = True
3842
3943 @classmethod
@@ -53,7 +57,7 @@ def get_retry_options(self):
5357 if retry_config_options :
5458 return {
5559 'total' :
56- retry_config_options .retry_total
60+ min ( retry_config_options .retry_total , self . MAX_TOTAL_RETRIES )
5761 if retry_config_options .retry_total is not None else self .total_retries ,
5862 'backoff' :
5963 retry_config_options .retry_backoff_factor
@@ -119,6 +123,8 @@ def should_retry(self, retry_options, response):
119123 """
120124 if not self ._is_method_retryable (retry_options , response .request ):
121125 return False
126+ if not self ._is_request_payload_buffered (response ):
127+ return False
122128 return retry_options ['total' ] and response .status_code in retry_options ['retry_codes' ]
123129
124130 def _is_method_retryable (self , retry_options , request ):
@@ -130,6 +136,18 @@ def _is_method_retryable(self, retry_options, request):
130136 return False
131137 return True
132138
139+ def _is_request_payload_buffered (self , response ):
140+ """
141+ Checks if the request payload is buffered/rewindable.
142+ Payloads with forward only streams will return false and have the responses
143+ returned without any retry attempt.
144+ """
145+ if response .request .method .upper () in frozenset (['HEAD' , 'GET' , 'DELETE' , 'OPTIONS' ]):
146+ return True
147+ if response .request .headers ['Content-Type' ] == "application/octet-stream" :
148+ return False
149+ return True
150+
133151 def increment_counter (self , retry_options ):
134152 """
135153 Increment the retry counters on every valid retry
0 commit comments