OLD | NEW |
1 # coding: utf-8 | 1 # coding: utf-8 |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 """Defines class Rietveld to easily access a rietveld instance. | 5 """Defines class Rietveld to easily access a rietveld instance. |
6 | 6 |
7 Security implications: | 7 Security implications: |
8 | 8 |
9 The following hypothesis are made: | 9 The following hypothesis are made: |
10 - Rietveld enforces: | 10 - Rietveld enforces: |
(...skipping 398 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
409 if m: | 409 if m: |
410 # Fake an HTTPError exception. Cheezy. :( | 410 # Fake an HTTPError exception. Cheezy. :( |
411 raise urllib2.HTTPError( | 411 raise urllib2.HTTPError( |
412 request_path, int(m.group(1)), msg, None, None) | 412 request_path, int(m.group(1)), msg, None, None) |
413 old_error_exit(msg) | 413 old_error_exit(msg) |
414 upload.ErrorExit = trap_http_500 | 414 upload.ErrorExit = trap_http_500 |
415 | 415 |
416 for retry in xrange(self._maxtries): | 416 for retry in xrange(self._maxtries): |
417 try: | 417 try: |
418 logging.debug('%s' % request_path) | 418 logging.debug('%s' % request_path) |
419 result = self.rpc_server.Send(request_path, **kwargs) | 419 return self.rpc_server.Send(request_path, **kwargs) |
420 # Sometimes GAE returns a HTTP 200 but with HTTP 500 as the content. | |
421 # How nice. | |
422 return result | |
423 except urllib2.HTTPError, e: | 420 except urllib2.HTTPError, e: |
424 if retry >= (self._maxtries - 1): | 421 if retry >= (self._maxtries - 1): |
425 raise | 422 raise |
426 flake_codes = [500, 502, 503] | 423 flake_codes = [500, 502, 503] |
427 if retry_on_404: | 424 if retry_on_404: |
428 flake_codes.append(404) | 425 flake_codes.append(404) |
429 if e.code not in flake_codes: | 426 if e.code not in flake_codes: |
430 raise | 427 raise |
431 except urllib2.URLError, e: | 428 except urllib2.URLError, e: |
432 if retry >= (self._maxtries - 1): | 429 if retry >= (self._maxtries - 1): |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 extra_headers=None, | 518 extra_headers=None, |
522 **kwargs): | 519 **kwargs): |
523 """Send a POST or GET request to the server. | 520 """Send a POST or GET request to the server. |
524 | 521 |
525 Args: | 522 Args: |
526 request_path: path on the server to hit. This is concatenated with the | 523 request_path: path on the server to hit. This is concatenated with the |
527 value of 'host' provided to the constructor. | 524 value of 'host' provided to the constructor. |
528 payload: request is a POST if not None, GET otherwise | 525 payload: request is a POST if not None, GET otherwise |
529 timeout: in seconds | 526 timeout: in seconds |
530 extra_headers: (dict) | 527 extra_headers: (dict) |
| 528 |
| 529 Returns: the HTTP response body as a string |
| 530 |
| 531 Raises: |
| 532 urllib2.HTTPError |
531 """ | 533 """ |
532 # This method signature should match upload.py:AbstractRpcServer.Send() | 534 # This method signature should match upload.py:AbstractRpcServer.Send() |
533 method = 'GET' | 535 method = 'GET' |
534 | 536 |
535 headers = self.extra_headers.copy() | 537 headers = self.extra_headers.copy() |
536 headers.update(extra_headers or {}) | 538 headers.update(extra_headers or {}) |
537 | 539 |
538 if payload is not None: | 540 if payload is not None: |
539 method = 'POST' | 541 method = 'POST' |
540 headers['Content-Type'] = content_type | 542 headers['Content-Type'] = content_type |
541 | 543 |
542 prev_timeout = self._http.timeout | 544 prev_timeout = self._http.timeout |
543 try: | 545 try: |
544 if timeout: | 546 if timeout: |
545 self._http.timeout = timeout | 547 self._http.timeout = timeout |
546 # TODO(pgervais) implement some kind of retry mechanism (see upload.py). | |
547 url = self.host + request_path | 548 url = self.host + request_path |
548 if kwargs: | 549 if kwargs: |
549 url += "?" + urllib.urlencode(kwargs) | 550 url += "?" + urllib.urlencode(kwargs) |
550 | 551 |
551 # This weird loop is there to detect when the OAuth2 token has expired. | 552 # This weird loop is there to detect when the OAuth2 token has expired. |
552 # This is specific to appengine *and* rietveld. It relies on the | 553 # This is specific to appengine *and* rietveld. It relies on the |
553 # assumption that a 302 is triggered only by an expired OAuth2 token. This | 554 # assumption that a 302 is triggered only by an expired OAuth2 token. This |
554 # prevents any usage of redirections in pages accessed this way. | 555 # prevents any usage of redirections in pages accessed this way. |
555 | 556 |
556 # This variable is used to make sure the following loop runs only twice. | 557 # This variable is used to make sure the following loop runs only twice. |
557 redirect_caught = False | 558 redirect_caught = False |
558 while True: | 559 while True: |
559 try: | 560 try: |
560 ret = self._http.request(url, | 561 ret = self._http.request(url, |
561 method=method, | 562 method=method, |
562 body=payload, | 563 body=payload, |
563 headers=headers, | 564 headers=headers, |
564 redirections=0) | 565 redirections=0) |
565 except httplib2.RedirectLimit: | 566 except httplib2.RedirectLimit: |
566 if redirect_caught or method != 'GET': | 567 if redirect_caught or method != 'GET': |
567 logging.error('Redirection detected after logging in. Giving up.') | 568 logging.error('Redirection detected after logging in. Giving up.') |
568 raise | 569 raise |
569 redirect_caught = True | 570 redirect_caught = True |
570 logging.debug('Redirection detected. Trying to log in again...') | 571 logging.debug('Redirection detected. Trying to log in again...') |
571 self.creds.access_token = None | 572 self.creds.access_token = None |
572 continue | 573 continue |
573 break | 574 break |
574 | 575 |
| 576 if ret[0].status != 200: |
| 577 raise urllib2.HTTPError( |
| 578 request_path, int(ret[0]['status']), ret[1], None, None) |
| 579 |
575 return ret[1] | 580 return ret[1] |
576 | 581 |
577 finally: | 582 finally: |
578 self._http.timeout = prev_timeout | 583 self._http.timeout = prev_timeout |
579 | 584 |
580 | 585 |
581 class JwtOAuth2Rietveld(Rietveld): | 586 class JwtOAuth2Rietveld(Rietveld): |
582 """Access to Rietveld using OAuth authentication. | 587 """Access to Rietveld using OAuth authentication. |
583 | 588 |
584 This class is supposed to be used only by bots, since this kind of | 589 This class is supposed to be used only by bots, since this kind of |
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
737 self, issue, patchset, reason, clobber, revision, builders_and_tests, | 742 self, issue, patchset, reason, clobber, revision, builders_and_tests, |
738 master=None, category='cq'): | 743 master=None, category='cq'): |
739 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 744 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
740 (builders_and_tests, issue)) | 745 (builders_and_tests, issue)) |
741 | 746 |
742 def trigger_distributed_try_jobs( # pylint:disable=R0201 | 747 def trigger_distributed_try_jobs( # pylint:disable=R0201 |
743 self, issue, patchset, reason, clobber, revision, masters, | 748 self, issue, patchset, reason, clobber, revision, masters, |
744 category='cq'): | 749 category='cq'): |
745 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % | 750 logging.info('ReadOnlyRietveld: triggering try jobs %r for issue %d' % |
746 (masters, issue)) | 751 (masters, issue)) |
OLD | NEW |