1#
2# Copyright 2015 Google Inc.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16"""Tests for http_wrapper."""
17import socket
18import unittest
19
20import httplib2
21from six.moves import http_client
22
23from mock import patch
24
25from apitools.base.py import exceptions
26from apitools.base.py import http_wrapper
27
28# pylint: disable=ungrouped-imports
29try:
30    from oauth2client.client import HttpAccessTokenRefreshError
31    from oauth2client.client import AccessTokenRefreshError
32    _TOKEN_REFRESH_STATUS_AVAILABLE = True
33except ImportError:
34    from oauth2client.client import AccessTokenRefreshError
35    _TOKEN_REFRESH_STATUS_AVAILABLE = False
36
37
38class _MockHttpRequest(object):
39
40    url = None
41
42
43class _MockHttpResponse(object):
44
45    def __init__(self, status_code):
46        self.response = {'status': status_code}
47
48
49class RaisesExceptionOnLen(object):
50
51    """Supports length property but raises if __len__ is used."""
52
53    def __len__(self):
54        raise Exception('len() called unnecessarily')
55
56    def length(self):
57        return 1
58
59
60class HttpWrapperTest(unittest.TestCase):
61
62    def testRequestBodyUsesLengthProperty(self):
63        http_wrapper.Request(body=RaisesExceptionOnLen())
64
65    def testRequestBodyWithLen(self):
66        http_wrapper.Request(body='burrito')
67
68    @unittest.skipIf(not _TOKEN_REFRESH_STATUS_AVAILABLE,
69                     'oauth2client<1.5 lacks HttpAccessTokenRefreshError.')
70    def testExceptionHandlerHttpAccessTokenError(self):
71        exception_arg = HttpAccessTokenRefreshError(status=503)
72        retry_args = http_wrapper.ExceptionRetryArgs(
73            http={'connections': {}}, http_request=_MockHttpRequest(),
74            exc=exception_arg, num_retries=0, max_retry_wait=0,
75            total_wait_sec=0)
76
77        # Disable time.sleep for this handler as it is called with
78        # a minimum value of 1 second.
79        with patch('time.sleep', return_value=None):
80            http_wrapper.HandleExceptionsAndRebuildHttpConnections(
81                retry_args)
82
83    @unittest.skipIf(not _TOKEN_REFRESH_STATUS_AVAILABLE,
84                     'oauth2client<1.5 lacks HttpAccessTokenRefreshError.')
85    def testExceptionHandlerHttpAccessTokenErrorRaises(self):
86        exception_arg = HttpAccessTokenRefreshError(status=200)
87        retry_args = http_wrapper.ExceptionRetryArgs(
88            http={'connections': {}}, http_request=_MockHttpRequest(),
89            exc=exception_arg, num_retries=0, max_retry_wait=0,
90            total_wait_sec=0)
91
92        # Disable time.sleep for this handler as it is called with
93        # a minimum value of 1 second.
94        with self.assertRaises(HttpAccessTokenRefreshError):
95            with patch('time.sleep', return_value=None):
96                http_wrapper.HandleExceptionsAndRebuildHttpConnections(
97                    retry_args)
98
99    def testExceptionHandlerAccessTokenErrorRaises(self):
100        exception_arg = AccessTokenRefreshError()
101        retry_args = http_wrapper.ExceptionRetryArgs(
102            http={'connections': {}}, http_request=_MockHttpRequest(),
103            exc=exception_arg, num_retries=0, max_retry_wait=0,
104            total_wait_sec=0)
105
106        # Disable time.sleep for this handler as it is called with
107        # a minimum value of 1 second.
108        with self.assertRaises(AccessTokenRefreshError):
109            with patch('time.sleep', return_value=None):
110                http_wrapper.HandleExceptionsAndRebuildHttpConnections(
111                    retry_args)
112
113    def testDefaultExceptionHandler(self):
114        """Ensures exception handles swallows (retries)"""
115        mock_http_content = 'content'.encode('utf8')
116        for exception_arg in (
117                http_client.BadStatusLine('line'),
118                http_client.IncompleteRead('partial'),
119                http_client.ResponseNotReady(),
120                socket.error(),
121                socket.gaierror(),
122                httplib2.ServerNotFoundError(),
123                ValueError(),
124                exceptions.RequestError(),
125                exceptions.BadStatusCodeError(
126                    {'status': 503}, mock_http_content, 'url'),
127                exceptions.RetryAfterError(
128                    {'status': 429}, mock_http_content, 'url', 0)):
129
130            retry_args = http_wrapper.ExceptionRetryArgs(
131                http={'connections': {}}, http_request=_MockHttpRequest(),
132                exc=exception_arg, num_retries=0, max_retry_wait=0,
133                total_wait_sec=0)
134
135            # Disable time.sleep for this handler as it is called with
136            # a minimum value of 1 second.
137            with patch('time.sleep', return_value=None):
138                http_wrapper.HandleExceptionsAndRebuildHttpConnections(
139                    retry_args)
140