| OLD | NEW |
| (Empty) |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import time | |
| 6 import urllib | |
| 7 | |
| 8 | |
| 9 _NO_RETRY_CODE = [200, 302, 401, 403, 404, 501] | |
| 10 | |
| 11 | |
| 12 class RetryHttpClient(object): | |
| 13 """Represents a http client to send http/https request to a remote server. | |
| 14 | |
| 15 Subclasses should implement abstract functions below. | |
| 16 """ | |
| 17 def __init__(self, no_error_logging_statuses=None): | |
| 18 # If an http request results in the given statuses, the subclasses should | |
| 19 # not log an error. | |
| 20 self.no_error_logging_statuses = no_error_logging_statuses | |
| 21 | |
| 22 def _Get(self, url, timeout_seconds, headers): # pylint: disable=W0613, R0201 | |
| 23 """Sends the actual HTTP GET request. | |
| 24 | |
| 25 Returns: | |
| 26 (status_code, content) | |
| 27 status_code: the HTTP status code of the response. | |
| 28 content: the content of the response. | |
| 29 """ | |
| 30 raise NotImplementedError( | |
| 31 '_Get() should be implemented in the child class') # pragma: no cover | |
| 32 | |
| 33 def _Post(self, url, data, timeout_seconds, | |
| 34 headers): # pylint: disable=W0613, R0201 | |
| 35 """Sends the actual HTTP POST request. | |
| 36 | |
| 37 Returns: | |
| 38 (status_code, content) | |
| 39 """ | |
| 40 raise NotImplementedError( | |
| 41 '_Post() should be implemented in the child class') # pragma: no cover | |
| 42 | |
| 43 def _Put(self, url, data, timeout_seconds, | |
| 44 headers): # pylint: disable=W0613, R0201 | |
| 45 """Sends the actual HTTP PUT request. | |
| 46 | |
| 47 Returns: | |
| 48 (status_code, content) | |
| 49 """ | |
| 50 raise NotImplementedError( | |
| 51 '_Put() should be implemented in the child class') # pragma: no cover | |
| 52 | |
| 53 def GetBackoff(self, retry_backoff, tries): | |
| 54 """Returns how many seconds to wait before next retry. | |
| 55 | |
| 56 When ``retry_backoff`` is more than 1, return an exponential backoff; | |
| 57 otherwise we keep it the same. | |
| 58 | |
| 59 Params: | |
| 60 retry_backoff (float): The base backoff in seconds. | |
| 61 tries (int): Indicates how many tries have been done. | |
| 62 """ | |
| 63 if retry_backoff > 1: | |
| 64 return retry_backoff * (2 ** (tries - 1)) | |
| 65 else: | |
| 66 return retry_backoff | |
| 67 | |
| 68 def _Retry(self, url, method, data=None, params=None, timeout_seconds=60, | |
| 69 max_retries=5, retry_backoff=1.5, headers=None): | |
| 70 if params and method == 'GET': | |
| 71 url = '%s?%s' % (url, urllib.urlencode(params)) | |
| 72 | |
| 73 tries = 0 | |
| 74 while tries < max_retries: | |
| 75 tries += 1 | |
| 76 | |
| 77 if method == 'POST': | |
| 78 status_code, content = self._Post(url, data, timeout_seconds, headers) | |
| 79 elif method == 'PUT': | |
| 80 status_code, content = self._Put(url, data, timeout_seconds, headers) | |
| 81 else: | |
| 82 status_code, content = self._Get(url, timeout_seconds, headers) | |
| 83 | |
| 84 if status_code in _NO_RETRY_CODE: | |
| 85 break | |
| 86 else: | |
| 87 time.sleep(self.GetBackoff(retry_backoff, tries)) | |
| 88 | |
| 89 return status_code, content | |
| 90 | |
| 91 def Get(self, url, params=None, timeout_seconds=60, | |
| 92 max_retries=5, retry_backoff=1.5, headers=None): | |
| 93 """Sends a GET request to the url with the given parameters and headers. | |
| 94 | |
| 95 Params: | |
| 96 url (str): The raw url to send request to. If ``params`` is specified, the | |
| 97 url should not include any parameter in it. | |
| 98 params (dict): A key-value dict of parameters to send in the request. | |
| 99 timeout_seconds (int): The timeout for read/write of the http request. | |
| 100 max_retries (int): The maxmium times of retries for the request when the | |
| 101 returning http status code is not in 200, 302, 401, 403, 404, or 501. | |
| 102 retry_backoff (float): The base backoff in seconds for retry. | |
| 103 | |
| 104 Returns: | |
| 105 (status_code, content) | |
| 106 """ | |
| 107 return self._Retry( | |
| 108 url, method='GET', data=None, params=params, | |
| 109 timeout_seconds=timeout_seconds, max_retries=max_retries, | |
| 110 retry_backoff=retry_backoff, headers=headers) | |
| 111 | |
| 112 def Post(self, url, data, timeout_seconds=60, | |
| 113 max_retries=5, retry_backoff=1.5, headers=None): | |
| 114 """Sends a POST request to the url with the given parameters and headers. | |
| 115 | |
| 116 Params: | |
| 117 url (str): The raw url to send request to. If ``params`` is specified, the | |
| 118 url should not include any parameter in it. | |
| 119 data (dict): The data to send for post request. | |
| 120 timeout_seconds (int): The timeout for read/write of the http request. | |
| 121 max_retries (int): The maximum times of retries for the request when the | |
| 122 returning http status code is not in 200, 302, 401, 403, 404, or 501. | |
| 123 retry_backoff (float): The base backoff in seconds for retry. | |
| 124 | |
| 125 Returns: | |
| 126 (status_code, content) | |
| 127 """ | |
| 128 return self._Retry( | |
| 129 url, method='POST', data=data, params=None, | |
| 130 timeout_seconds=timeout_seconds, max_retries=max_retries, | |
| 131 retry_backoff=retry_backoff, headers=headers) | |
| 132 | |
| 133 def Put(self, url, data, timeout_seconds=60, | |
| 134 max_retries=5, retry_backoff=1.5, headers=None): | |
| 135 """Sends a PUT request to the url with the given parameters and headers. | |
| 136 | |
| 137 Params: | |
| 138 url (str): The raw url to send request to. If ``params`` is specified, the | |
| 139 url should not include any parameter in it. | |
| 140 data (dict): The data to send for post request. | |
| 141 timeout_seconds (int): The timeout for read/write of the http request. | |
| 142 max_retries (int): The maximum times of retries for the request when the | |
| 143 returning http status code is not in 200, 302, 401, 403, 404, or 501. | |
| 144 retry_backoff (float): The base backoff in seconds for retry. | |
| 145 | |
| 146 Returns: | |
| 147 (status_code, content) | |
| 148 """ | |
| 149 return self._Retry( | |
| 150 url, method='PUT', data=data, params=None, | |
| 151 timeout_seconds=timeout_seconds, max_retries=max_retries, | |
| 152 retry_backoff=retry_backoff, headers=headers) | |
| OLD | NEW |