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 |