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