OLD | NEW |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import collections | 5 import collections |
6 import copy | 6 import copy |
7 import json | 7 import json |
8 import logging | 8 import logging |
9 import os | 9 import os |
10 import re | 10 import re |
(...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 | 158 |
159 if http_identifier: | 159 if http_identifier: |
160 http = InstrumentedHttp(http_identifier, timeout=timeout) | 160 http = InstrumentedHttp(http_identifier, timeout=timeout) |
161 else: | 161 else: |
162 http = httplib2.Http(timeout=timeout) | 162 http = httplib2.Http(timeout=timeout) |
163 return creds.authorize(http) | 163 return creds.authorize(http) |
164 | 164 |
165 class RetriableHttp(object): | 165 class RetriableHttp(object): |
166 """A httplib2.Http object that retries on failure.""" | 166 """A httplib2.Http object that retries on failure.""" |
167 | 167 |
168 def __init__(self, http, max_tries=5, retrying_statuses_fn=None): | 168 def __init__(self, http, max_tries=5, backoff_time=1, |
| 169 retrying_statuses_fn=None): |
169 """ | 170 """ |
170 Args: | 171 Args: |
171 http: an httplib2.Http instance | 172 http: an httplib2.Http instance |
172 max_tries: a number of maximum tries | 173 max_tries: a number of maximum tries |
| 174 backoff_time: a number of seconds to sleep between retries |
173 retrying_statuses_fn: a function that returns True if a given status | 175 retrying_statuses_fn: a function that returns True if a given status |
174 should be retried | 176 should be retried |
175 """ | 177 """ |
176 self._http = http | 178 self._http = http |
177 self._max_tries = max_tries | 179 self._max_tries = max_tries |
| 180 self._backoff_time = backoff_time |
178 self._retrying_statuses_fn = retrying_statuses_fn or \ | 181 self._retrying_statuses_fn = retrying_statuses_fn or \ |
179 set(range(500,599)).__contains__ | 182 set(range(500,599)).__contains__ |
180 | 183 |
181 def request(self, uri, method='GET', body=None, *args, **kwargs): | 184 def request(self, uri, method='GET', body=None, *args, **kwargs): |
182 for i in range(1, self._max_tries + 1): | 185 for i in range(1, self._max_tries + 1): |
183 try: | 186 try: |
184 response, content = self._http.request(uri, method, body, *args, | 187 response, content = self._http.request(uri, method, body, *args, |
185 **kwargs) | 188 **kwargs) |
186 | 189 |
187 if self._retrying_statuses_fn(response.status): | 190 if self._retrying_statuses_fn(response.status): |
188 logging.info('RetriableHttp: attempt %d receiving status %d, %s', | 191 logging.info('RetriableHttp: attempt %d receiving status %d, %s', |
189 i, response.status, | 192 i, response.status, |
190 'final attempt' if i == self._max_tries else \ | 193 'final attempt' if i == self._max_tries else \ |
191 'will retry') | 194 'will retry') |
192 else: | 195 else: |
193 break | 196 break |
194 except (ValueError, errors.Error, | 197 except (ValueError, errors.Error, |
195 socket.timeout, socket.error, socket.herror, socket.gaierror, | 198 socket.timeout, socket.error, socket.herror, socket.gaierror, |
196 httplib2.HttpLib2Error) as error: | 199 httplib2.HttpLib2Error) as error: |
197 logging.info('RetriableHttp: attempt %d received exception: %s, %s', | 200 logging.info('RetriableHttp: attempt %d received exception: %s, %s', |
198 i, error, 'final attempt' if i == self._max_tries else \ | 201 i, error, 'final attempt' if i == self._max_tries else \ |
199 'will retry') | 202 'will retry') |
200 if i == self._max_tries: | 203 if i == self._max_tries: |
201 raise | 204 raise |
| 205 time.sleep(self._backoff_time) |
202 | 206 |
203 return response, content | 207 return response, content |
204 | 208 |
205 def __getattr__(self, name): | 209 def __getattr__(self, name): |
206 return getattr(self._http, name) | 210 return getattr(self._http, name) |
207 | 211 |
208 def __setattr__(self, name, value): | 212 def __setattr__(self, name, value): |
209 if name in ('request', '_http', '_max_tries', '_retrying_statuses_fn'): | 213 if name in ('request', '_http', '_max_tries', '_backoff_time', |
| 214 '_retrying_statuses_fn'): |
210 self.__dict__[name] = value | 215 self.__dict__[name] = value |
211 else: | 216 else: |
212 setattr(self._http, name, value) | 217 setattr(self._http, name, value) |
213 | 218 |
214 class InstrumentedHttp(httplib2.Http): | 219 class InstrumentedHttp(httplib2.Http): |
215 """A httplib2.Http object that reports ts_mon metrics about its requests.""" | 220 """A httplib2.Http object that reports ts_mon metrics about its requests.""" |
216 | 221 |
217 def __init__(self, name, time_fn=time.time, timeout=DEFAULT_TIMEOUT, | 222 def __init__(self, name, time_fn=time.time, timeout=DEFAULT_TIMEOUT, |
218 **kwargs): | 223 **kwargs): |
219 """ | 224 """ |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
306 self.requests_made.append(self.HttpCall(uri, method, body, headers)) | 311 self.requests_made.append(self.HttpCall(uri, method, body, headers)) |
307 headers = None | 312 headers = None |
308 body = None | 313 body = None |
309 for candidate in self._uris: | 314 for candidate in self._uris: |
310 if candidate[0].match(uri): | 315 if candidate[0].match(uri): |
311 _, headers, body = candidate | 316 _, headers, body = candidate |
312 break | 317 break |
313 if not headers: | 318 if not headers: |
314 raise AssertionError("Unexpected request to %s" % uri) | 319 raise AssertionError("Unexpected request to %s" % uri) |
315 return httplib2.Response(headers), body | 320 return httplib2.Response(headers), body |
OLD | NEW |