Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/google/resumable_media/requests/_request_helpers.py: 6%
49 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-20 06:09 +0000
1# Copyright 2017 Google Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
15"""Shared utilities used by both downloads and uploads.
17This utilities are explicitly catered to ``requests``-like transports.
18"""
20import http.client
21import requests.exceptions
22import urllib3.exceptions # type: ignore
24import time
26from google.resumable_media import common
27from google.resumable_media import _helpers
29_DEFAULT_RETRY_STRATEGY = common.RetryStrategy()
30_SINGLE_GET_CHUNK_SIZE = 8192
31# The number of seconds to wait to establish a connection
32# (connect() call on socket). Avoid setting this to a multiple of 3 to not
33# Align with TCP Retransmission timing. (typically 2.5-3s)
34_DEFAULT_CONNECT_TIMEOUT = 61
35# The number of seconds to wait between bytes sent from the server.
36_DEFAULT_READ_TIMEOUT = 60
38_CONNECTION_ERROR_CLASSES = (
39 http.client.BadStatusLine,
40 http.client.IncompleteRead,
41 http.client.ResponseNotReady,
42 requests.exceptions.ConnectionError,
43 requests.exceptions.ChunkedEncodingError,
44 requests.exceptions.Timeout,
45 urllib3.exceptions.PoolError,
46 urllib3.exceptions.ProtocolError,
47 urllib3.exceptions.SSLError,
48 urllib3.exceptions.TimeoutError,
49 ConnectionError, # Python 3.x only, superclass of ConnectionResetError.
50)
53class RequestsMixin(object):
54 """Mix-in class implementing ``requests``-specific behavior.
56 These are methods that are more general purpose, with implementations
57 specific to the types defined in ``requests``.
58 """
60 @staticmethod
61 def _get_status_code(response):
62 """Access the status code from an HTTP response.
64 Args:
65 response (~requests.Response): The HTTP response object.
67 Returns:
68 int: The status code.
69 """
70 return response.status_code
72 @staticmethod
73 def _get_headers(response):
74 """Access the headers from an HTTP response.
76 Args:
77 response (~requests.Response): The HTTP response object.
79 Returns:
80 ~requests.structures.CaseInsensitiveDict: The header mapping (keys
81 are case-insensitive).
82 """
83 return response.headers
85 @staticmethod
86 def _get_body(response):
87 """Access the response body from an HTTP response.
89 Args:
90 response (~requests.Response): The HTTP response object.
92 Returns:
93 bytes: The body of the ``response``.
94 """
95 return response.content
98class RawRequestsMixin(RequestsMixin):
99 @staticmethod
100 def _get_body(response):
101 """Access the response body from an HTTP response.
103 Args:
104 response (~requests.Response): The HTTP response object.
106 Returns:
107 bytes: The body of the ``response``.
108 """
109 if response._content is False:
110 response._content = b"".join(
111 response.raw.stream(_SINGLE_GET_CHUNK_SIZE, decode_content=False)
112 )
113 response._content_consumed = True
114 return response._content
117def wait_and_retry(func, get_status_code, retry_strategy):
118 """Attempts to retry a call to ``func`` until success.
120 Expects ``func`` to return an HTTP response and uses ``get_status_code``
121 to check if the response is retry-able.
123 ``func`` is expected to raise a failure status code as a
124 common.InvalidResponse, at which point this method will check the code
125 against the common.RETRIABLE list of retriable status codes.
127 Will retry until :meth:`~.RetryStrategy.retry_allowed` (on the current
128 ``retry_strategy``) returns :data:`False`. Uses
129 :func:`_helpers.calculate_retry_wait` to double the wait time (with jitter)
130 after each attempt.
132 Args:
133 func (Callable): A callable that takes no arguments and produces
134 an HTTP response which will be checked as retry-able.
135 get_status_code (Callable[Any, int]): Helper to get a status code
136 from a response.
137 retry_strategy (~google.resumable_media.common.RetryStrategy): The
138 strategy to use if the request fails and must be retried.
140 Returns:
141 object: The return value of ``func``.
142 """
143 total_sleep = 0.0
144 num_retries = 0
145 # base_wait will be multiplied by the multiplier on the first retry.
146 base_wait = float(retry_strategy.initial_delay) / retry_strategy.multiplier
148 # Set the retriable_exception_type if possible. We expect requests to be
149 # present here and the transport to be using requests.exceptions errors,
150 # but due to loose coupling with the transport layer we can't guarantee it.
152 while True: # return on success or when retries exhausted.
153 error = None
154 try:
155 response = func()
156 except _CONNECTION_ERROR_CLASSES as e:
157 error = e # Fall through to retry, if there are retries left.
158 except common.InvalidResponse as e:
159 # An InvalidResponse is only retriable if its status code matches.
160 # The `process_response()` method on a Download or Upload method
161 # will convert the status code into an exception.
162 if get_status_code(e.response) in common.RETRYABLE:
163 error = e # Fall through to retry, if there are retries left.
164 else:
165 raise # If the status code is not retriable, raise w/o retry.
166 else:
167 return response
169 base_wait, wait_time = _helpers.calculate_retry_wait(
170 base_wait, retry_strategy.max_sleep, retry_strategy.multiplier
171 )
172 num_retries += 1
173 total_sleep += wait_time
175 # Check if (another) retry is allowed. If retries are exhausted and
176 # no acceptable response was received, raise the retriable error.
177 if not retry_strategy.retry_allowed(total_sleep, num_retries):
178 raise error
180 time.sleep(wait_time)